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 ##############################################################################
14 from cinderclient import client as cinderclient
15 from novaclient import client as novaclient
16 from glanceclient import client as glanceclient
17 from keystoneauth1 import loading
18 from keystoneauth1 import session
19 from neutronclient.neutron import client as neutronclient
23 from yardstick.common import constants
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(**os_cloud_config):
160 """Get Shade OpenStack cloud client
162 By default, the input parameters given to "shade.openstack_cloud" method
163 are stored in "constants.OS_CLOUD_DEFAULT_CONFIG". The input parameters
164 passed in this function, "os_cloud_config", will overwrite the default
167 :param os_cloud_config: (kwargs) input arguments for
168 "shade.openstack_cloud" method.
169 :return: ``shade.OpenStackCloud`` object.
171 params = copy.deepcopy(constants.OS_CLOUD_DEFAULT_CONFIG)
172 params.update(os_cloud_config)
173 return shade.openstack_cloud(**params)
175 def get_shade_operator_client(**os_cloud_config):
176 """Get Shade Operator cloud client
178 :return: ``shade.OperatorCloud`` object.
180 params = copy.deepcopy(constants.OS_CLOUD_DEFAULT_CONFIG)
181 params.update(os_cloud_config)
182 return shade.operator_cloud(**params)
185 # *********************************************
187 # *********************************************
188 def create_keypair(shade_client, name, public_key=None):
189 """Create a new keypair.
191 :param name: Name of the keypair being created.
192 :param public_key: Public key for the new keypair.
194 :return: Created keypair.
197 return shade_client.create_keypair(name, public_key=public_key)
198 except exc.OpenStackCloudException as o_exc:
199 log.error("Error [create_keypair(shade_client)]. "
200 "Exception message, '%s'", o_exc.orig_message)
203 def create_instance_and_wait_for_active(shade_client, name, image,
204 flavor, auto_ip=True, ips=None,
205 ip_pool=None, root_volume=None,
206 terminate_volume=False, wait=True,
207 timeout=180, reuse_ips=True,
208 network=None, boot_from_volume=False,
209 volume_size='20', boot_volume=None,
210 volumes=None, nat_destination=None,
212 """Create a virtual server instance.
214 :param name:(string) Name of the server.
215 :param image:(dict) Image dict, name or ID to boot with. Image is required
216 unless boot_volume is given.
217 :param flavor:(dict) Flavor dict, name or ID to boot onto.
218 :param auto_ip: Whether to take actions to find a routable IP for
220 :param ips: List of IPs to attach to the server.
221 :param ip_pool:(string) Name of the network or floating IP pool to get an
223 :param root_volume:(string) Name or ID of a volume to boot from.
224 (defaults to None - deprecated, use boot_volume)
225 :param boot_volume:(string) Name or ID of a volume to boot from.
226 :param terminate_volume:(bool) If booting from a volume, whether it should
227 be deleted when the server is destroyed.
228 :param volumes:(optional) A list of volumes to attach to the server.
229 :param wait:(optional) Wait for the address to appear as assigned to the server.
230 :param timeout: Seconds to wait, defaults to 60.
231 :param reuse_ips:(bool)Whether to attempt to reuse pre-existing
232 floating ips should a floating IP be needed.
233 :param network:(dict) Network dict or name or ID to attach the server to.
234 Mutually exclusive with the nics parameter. Can also be be
235 a list of network names or IDs or network dicts.
236 :param boot_from_volume:(bool) Whether to boot from volume. 'boot_volume'
237 implies True, but boot_from_volume=True with
238 no boot_volume is valid and will create a
239 volume from the image and use that.
240 :param volume_size: When booting an image from volume, how big should
241 the created volume be?
242 :param nat_destination: Which network should a created floating IP
243 be attached to, if it's not possible to infer from
244 the cloud's configuration.
245 :param meta:(optional) A dict of arbitrary key/value metadata to store for
246 this server. Both keys and values must be <=255 characters.
247 :param reservation_id: A UUID for the set of servers being requested.
248 :param min_count:(optional extension) The minimum number of servers to
250 :param max_count:(optional extension) The maximum number of servers to
252 :param security_groups: A list of security group names.
253 :param userdata: User data to pass to be exposed by the metadata server
254 this can be a file type object as well or a string.
255 :param key_name:(optional extension) Name of previously created keypair to
256 inject into the instance.
257 :param availability_zone: Name of the availability zone for instance
259 :param block_device_mapping:(optional) A dict of block device mappings for
261 :param block_device_mapping_v2:(optional) A dict of block device mappings
263 :param nics:(optional extension) An ordered list of nics to be added to
264 this server, with information about connected networks, fixed
266 :param scheduler_hints:(optional extension) Arbitrary key-value pairs
267 specified by the client to help boot an instance.
268 :param config_drive:(optional extension) Value for config drive either
269 boolean, or volume-id.
270 :param disk_config:(optional extension) Control how the disk is partitioned
271 when the server is created. Possible values are 'AUTO'
273 :param admin_pass:(optional extension) Add a user supplied admin password.
275 :returns: The created server.
278 return shade_client.create_server(
279 name, image, flavor, auto_ip=auto_ip, ips=ips, ip_pool=ip_pool,
280 root_volume=root_volume, terminate_volume=terminate_volume,
281 wait=wait, timeout=timeout, reuse_ips=reuse_ips, network=network,
282 boot_from_volume=boot_from_volume, volume_size=volume_size,
283 boot_volume=boot_volume, volumes=volumes,
284 nat_destination=nat_destination, **kwargs)
285 except exc.OpenStackCloudException as o_exc:
286 log.error("Error [create_instance(shade_client)]. "
287 "Exception message, '%s'", o_exc.orig_message)
290 def attach_volume_to_server(shade_client, server_name_or_id, volume_name_or_id,
291 device=None, wait=True, timeout=None):
292 """Attach a volume to a server.
294 This will attach a volume, described by the passed in volume
295 dict, to the server described by the passed in server dict on the named
296 device on the server.
298 If the volume is already attached to the server, or generally not
299 available, then an exception is raised. To re-attach to a server,
300 but under a different device, the user must detach it first.
302 :param server_name_or_id:(string) The server name or id to attach to.
303 :param volume_name_or_id:(string) The volume name or id to attach.
304 :param device:(string) The device name where the volume will attach.
305 :param wait:(bool) If true, waits for volume to be attached.
306 :param timeout: Seconds to wait for volume attachment. None is forever.
308 :returns: True if attached successful, False otherwise.
311 server = shade_client.get_server(name_or_id=server_name_or_id)
312 volume = shade_client.get_volume(volume_name_or_id)
313 shade_client.attach_volume(
314 server, volume, device=device, wait=wait, timeout=timeout)
316 except exc.OpenStackCloudException as o_exc:
317 log.error("Error [attach_volume_to_server(shade_client)]. "
318 "Exception message: %s", o_exc.orig_message)
322 def delete_instance(shade_client, name_or_id, wait=False, timeout=180,
323 delete_ips=False, delete_ip_retry=1):
324 """Delete a server instance.
326 :param name_or_id: name or ID of the server to delete
327 :param wait:(bool) If true, waits for server to be deleted.
328 :param timeout:(int) Seconds to wait for server deletion.
329 :param delete_ips:(bool) If true, deletes any floating IPs associated with
331 :param delete_ip_retry:(int) Number of times to retry deleting
332 any floating ips, should the first try be
334 :returns: True if delete succeeded, False otherwise.
337 return shade_client.delete_server(
338 name_or_id, wait=wait, timeout=timeout, delete_ips=delete_ips,
339 delete_ip_retry=delete_ip_retry)
340 except exc.OpenStackCloudException as o_exc:
341 log.error("Error [delete_instance(shade_client, '%s')]. "
342 "Exception message: %s", name_or_id,
347 def get_server(shade_client, name_or_id=None, filters=None, detailed=False,
349 """Get a server by name or ID.
351 :param name_or_id: Name or ID of the server.
352 :param filters:(dict) A dictionary of meta data to use for further
354 :param detailed:(bool) Whether or not to add detailed additional
356 :param bare:(bool) Whether to skip adding any additional information to the
359 :returns: A server ``munch.Munch`` or None if no matching server is found.
362 return shade_client.get_server(name_or_id=name_or_id, filters=filters,
363 detailed=detailed, bare=bare)
364 except exc.OpenStackCloudException as o_exc:
365 log.error("Error [get_server(shade_client, '%s')]. "
366 "Exception message: %s", name_or_id, o_exc.orig_message)
369 def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover
371 return get_nova_client().flavors.create(name, ram, vcpus,
373 except Exception: # pylint: disable=broad-except
374 log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]",
375 name, ram, disk, vcpus, kwargs['is_public'])
379 def get_flavor_id(nova_client, flavor_name): # pragma: no cover
380 flavors = nova_client.flavors.list(detailed=True)
383 if f.name == flavor_name:
389 def get_flavor(shade_client, name_or_id, filters=None, get_extra=True):
390 """Get a flavor by name or ID.
392 :param name_or_id: Name or ID of the flavor.
393 :param filters: A dictionary of meta data to use for further filtering.
394 :param get_extra: Whether or not the list_flavors call should get the extra
397 :returns: A flavor ``munch.Munch`` or None if no matching flavor is found.
400 return shade_client.get_flavor(name_or_id, filters=filters,
402 except exc.OpenStackCloudException as o_exc:
403 log.error("Error [get_flavor(shade_client, '%s')]. "
404 "Exception message: %s", name_or_id, o_exc.orig_message)
407 def delete_flavor(flavor_id): # pragma: no cover
409 get_nova_client().flavors.delete(flavor_id)
410 except Exception: # pylint: disable=broad-except
411 log.exception("Error [delete_flavor(nova_client, %s)]", flavor_id)
417 def delete_keypair(shade_client, name):
420 :param name: Name of the keypair to delete.
422 :returns: True if delete succeeded, False otherwise.
425 return shade_client.delete_keypair(name)
426 except exc.OpenStackCloudException as o_exc:
427 log.error("Error [delete_neutron_router(shade_client, '%s')]. "
428 "Exception message: %s", name, o_exc.orig_message)
432 # *********************************************
434 # *********************************************
435 def create_neutron_net(shade_client, network_name, shared=False,
436 admin_state_up=True, external=False, provider=None,
438 """Create a neutron network.
440 :param network_name:(string) name of the network being created.
441 :param shared:(bool) whether the network is shared.
442 :param admin_state_up:(bool) set the network administrative state.
443 :param external:(bool) whether this network is externally accessible.
444 :param provider:(dict) a dict of network provider options.
445 :param project_id:(string) specify the project ID this network
446 will be created on (admin-only).
447 :returns:(string) the network id.
450 networks = shade_client.create_network(
451 name=network_name, shared=shared, admin_state_up=admin_state_up,
452 external=external, provider=provider, project_id=project_id)
453 return networks['id']
454 except exc.OpenStackCloudException as o_exc:
455 log.error("Error [create_neutron_net(shade_client)]."
456 "Exception message, '%s'", o_exc.orig_message)
460 def delete_neutron_net(shade_client, network_id):
462 return shade_client.delete_network(network_id)
463 except exc.OpenStackCloudException:
464 log.error("Error [delete_neutron_net(shade_client, '%s')]", network_id)
468 def create_neutron_subnet(shade_client, network_name_or_id, cidr=None,
469 ip_version=4, enable_dhcp=False, subnet_name=None,
470 tenant_id=None, allocation_pools=None,
471 gateway_ip=None, disable_gateway_ip=False,
472 dns_nameservers=None, host_routes=None,
473 ipv6_ra_mode=None, ipv6_address_mode=None,
474 use_default_subnetpool=False):
475 """Create a subnet on a specified network.
477 :param network_name_or_id:(string) the unique name or ID of the
478 attached network. If a non-unique name is
479 supplied, an exception is raised.
480 :param cidr:(string) the CIDR.
481 :param ip_version:(int) the IP version.
482 :param enable_dhcp:(bool) whether DHCP is enable.
483 :param subnet_name:(string) the name of the subnet.
484 :param tenant_id:(string) the ID of the tenant who owns the network.
485 :param allocation_pools: A list of dictionaries of the start and end
486 addresses for the allocation pools.
487 :param gateway_ip:(string) the gateway IP address.
488 :param disable_gateway_ip:(bool) whether gateway IP address is enabled.
489 :param dns_nameservers: A list of DNS name servers for the subnet.
490 :param host_routes: A list of host route dictionaries for the subnet.
491 :param ipv6_ra_mode:(string) IPv6 Router Advertisement mode.
492 Valid values are: 'dhcpv6-stateful',
493 'dhcpv6-stateless', or 'slaac'.
494 :param ipv6_address_mode:(string) IPv6 address mode.
495 Valid values are: 'dhcpv6-stateful',
496 'dhcpv6-stateless', or 'slaac'.
497 :param use_default_subnetpool:(bool) use the default subnetpool for
498 ``ip_version`` to obtain a CIDR. It is
499 required to pass ``None`` to the ``cidr``
500 argument when enabling this option.
501 :returns:(string) the subnet id.
504 subnet = shade_client.create_subnet(
505 network_name_or_id, cidr=cidr, ip_version=ip_version,
506 enable_dhcp=enable_dhcp, subnet_name=subnet_name,
507 tenant_id=tenant_id, allocation_pools=allocation_pools,
508 gateway_ip=gateway_ip, disable_gateway_ip=disable_gateway_ip,
509 dns_nameservers=dns_nameservers, host_routes=host_routes,
510 ipv6_ra_mode=ipv6_ra_mode, ipv6_address_mode=ipv6_address_mode,
511 use_default_subnetpool=use_default_subnetpool)
513 except exc.OpenStackCloudException as o_exc:
514 log.error("Error [create_neutron_subnet(shade_client)]. "
515 "Exception message: %s", o_exc.orig_message)
519 def create_neutron_router(shade_client, name=None, admin_state_up=True,
520 ext_gateway_net_id=None, enable_snat=None,
521 ext_fixed_ips=None, project_id=None):
522 """Create a logical router.
524 :param name:(string) the router name.
525 :param admin_state_up:(bool) the administrative state of the router.
526 :param ext_gateway_net_id:(string) network ID for the external gateway.
527 :param enable_snat:(bool) enable Source NAT (SNAT) attribute.
528 :param ext_fixed_ips: List of dictionaries of desired IP and/or subnet
529 on the external network.
530 :param project_id:(string) project ID for the router.
532 :returns:(string) the router id.
535 router = shade_client.create_router(
536 name, admin_state_up, ext_gateway_net_id, enable_snat,
537 ext_fixed_ips, project_id)
539 except exc.OpenStackCloudException as o_exc:
540 log.error("Error [create_neutron_router(shade_client)]. "
541 "Exception message: %s", o_exc.orig_message)
544 def delete_neutron_router(shade_client, router_id):
546 return shade_client.delete_router(router_id)
547 except exc.OpenStackCloudException as o_exc:
548 log.error("Error [delete_neutron_router(shade_client, '%s')]. "
549 "Exception message: %s", router_id, o_exc.orig_message)
553 def remove_gateway_router(neutron_client, router_id): # pragma: no cover
555 neutron_client.remove_gateway_router(router_id)
557 except Exception: # pylint: disable=broad-except
558 log.error("Error [remove_gateway_router(neutron_client, '%s')]",
563 def remove_router_interface(shade_client, router, subnet_id=None,
565 """Detach a subnet from an internal router interface.
567 At least one of subnet_id or port_id must be supplied. If you specify both
568 subnet and port ID, the subnet ID must correspond to the subnet ID of the
569 first IP address on the port specified by the port ID.
570 Otherwise an error occurs.
572 :param router: The dict object of the router being changed
573 :param subnet_id:(string) The ID of the subnet to use for the interface
574 :param port_id:(string) The ID of the port to use for the interface
575 :returns: True on success
578 shade_client.remove_router_interface(
579 router, subnet_id=subnet_id, port_id=port_id)
581 except exc.OpenStackCloudException as o_exc:
582 log.error("Error [remove_interface_router(shade_client)]. "
583 "Exception message: %s", o_exc.orig_message)
587 def create_floating_ip(shade_client, network_name_or_id=None, server=None,
588 fixed_address=None, nat_destination=None,
589 port=None, wait=False, timeout=60):
590 """Allocate a new floating IP from a network or a pool.
592 :param network_name_or_id: Name or ID of the network
593 that the floating IP should come from.
594 :param server: Server dict for the server to create
595 the IP for and to which it should be attached.
596 :param fixed_address: Fixed IP to attach the floating ip to.
597 :param nat_destination: Name or ID of the network
598 that the fixed IP to attach the floating
600 :param port: The port ID that the floating IP should be
601 attached to. Specifying a port conflicts with specifying a
602 server,fixed_address or nat_destination.
603 :param wait: Whether to wait for the IP to be active.Only applies
604 if a server is provided.
605 :param timeout: How long to wait for the IP to be active.Only
606 applies if a server is provided.
608 :returns:Floating IP id and address
611 fip = shade_client.create_floating_ip(
612 network=network_name_or_id, server=server,
613 fixed_address=fixed_address, nat_destination=nat_destination,
614 port=port, wait=wait, timeout=timeout)
615 return {'fip_addr': fip['floating_ip_address'], 'fip_id': fip['id']}
616 except exc.OpenStackCloudException as o_exc:
617 log.error("Error [create_floating_ip(shade_client)]. "
618 "Exception message: %s", o_exc.orig_message)
621 def delete_floating_ip(shade_client, floating_ip_id, retry=1):
623 return shade_client.delete_floating_ip(floating_ip_id=floating_ip_id,
625 except exc.OpenStackCloudException as o_exc:
626 log.error("Error [delete_floating_ip(shade_client,'%s')]. "
627 "Exception message: %s", floating_ip_id, o_exc.orig_message)
631 def create_security_group_rule(shade_client, secgroup_name_or_id,
632 port_range_min=None, port_range_max=None,
633 protocol=None, remote_ip_prefix=None,
634 remote_group_id=None, direction='ingress',
635 ethertype='IPv4', project_id=None):
636 """Create a new security group rule
638 :param secgroup_name_or_id:(string) The security group name or ID to
639 associate with this security group rule. If a
640 non-unique group name is given, an exception is
642 :param port_range_min:(int) The minimum port number in the range that is
643 matched by the security group rule. If the protocol
644 is TCP or UDP, this value must be less than or equal
645 to the port_range_max attribute value. If nova is
646 used by the cloud provider for security groups, then
647 a value of None will be transformed to -1.
648 :param port_range_max:(int) The maximum port number in the range that is
649 matched by the security group rule. The
650 port_range_min attribute constrains the
651 port_range_max attribute. If nova is used by the
652 cloud provider for security groups, then a value of
653 None will be transformed to -1.
654 :param protocol:(string) The protocol that is matched by the security group
655 rule. Valid values are None, tcp, udp, and icmp.
656 :param remote_ip_prefix:(string) The remote IP prefix to be associated with
657 this security group rule. This attribute matches
658 the specified IP prefix as the source IP address of
660 :param remote_group_id:(string) The remote group ID to be associated with
661 this security group rule.
662 :param direction:(string) Ingress or egress: The direction in which the
663 security group rule is applied.
664 :param ethertype:(string) Must be IPv4 or IPv6, and addresses represented
665 in CIDR must match the ingress or egress rules.
666 :param project_id:(string) Specify the project ID this security group will
667 be created on (admin-only).
669 :returns: True on success.
673 shade_client.create_security_group_rule(
674 secgroup_name_or_id, port_range_min=port_range_min,
675 port_range_max=port_range_max, protocol=protocol,
676 remote_ip_prefix=remote_ip_prefix, remote_group_id=remote_group_id,
677 direction=direction, ethertype=ethertype, project_id=project_id)
679 except exc.OpenStackCloudException as op_exc:
680 log.error("Failed to create_security_group_rule(shade_client). "
681 "Exception message: %s", op_exc.orig_message)
685 def create_security_group_full(shade_client, sg_name,
686 sg_description, project_id=None):
687 security_group = shade_client.get_security_group(sg_name)
690 log.info("Using existing security group '%s'...", sg_name)
691 return security_group['id']
693 log.info("Creating security group '%s'...", sg_name)
695 security_group = shade_client.create_security_group(
696 sg_name, sg_description, project_id=project_id)
697 except (exc.OpenStackCloudException,
698 exc.OpenStackCloudUnavailableFeature) as op_exc:
699 log.error("Error [create_security_group(shade_client, %s, %s)]. "
700 "Exception message: %s", sg_name, sg_description,
704 log.debug("Security group '%s' with ID=%s created successfully.",
705 security_group['name'], security_group['id'])
707 log.debug("Adding ICMP rules in security group '%s'...", sg_name)
708 if not create_security_group_rule(shade_client, security_group['id'],
709 direction='ingress', protocol='icmp'):
710 log.error("Failed to create the security group rule...")
711 shade_client.delete_security_group(sg_name)
714 log.debug("Adding SSH rules in security group '%s'...", sg_name)
715 if not create_security_group_rule(shade_client, security_group['id'],
716 direction='ingress', 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)
723 if not create_security_group_rule(shade_client, security_group['id'],
724 direction='egress', protocol='tcp',
726 port_range_max='22'):
727 log.error("Failed to create the security group rule...")
728 shade_client.delete_security_group(sg_name)
730 return security_group['id']
733 # *********************************************
735 # *********************************************
736 def create_image(shade_client, name, filename=None, container='images',
737 md5=None, sha256=None, disk_format=None,
738 container_format=None, disable_vendor_agent=True,
739 wait=False, timeout=3600, allow_duplicates=False, meta=None,
740 volume=None, **kwargs):
743 :param name:(str) Name of the image to create. If it is a pathname of an
744 image, the name will be constructed from the extensionless
745 basename of the path.
746 :param filename:(str) The path to the file to upload, if needed.
747 :param container:(str) Name of the container in swift where images should
748 be uploaded for import if the cloud requires such a thing.
749 :param md5:(str) md5 sum of the image file. If not given, an md5 will
751 :param sha256:(str) sha256 sum of the image file. If not given, an md5
753 :param disk_format:(str) The disk format the image is in.
754 :param container_format:(str) The container format the image is in.
755 :param disable_vendor_agent:(bool) Whether or not to append metadata
756 flags to the image to inform the cloud in
757 question to not expect a vendor agent to be running.
758 :param wait:(bool) If true, waits for image to be created.
759 :param timeout:(str) Seconds to wait for image creation.
760 :param allow_duplicates:(bool) If true, skips checks that enforce unique
762 :param meta:(dict) A dict of key/value pairs to use for metadata that
763 bypasses automatic type conversion.
764 :param volume:(str) Name or ID or volume object of a volume to create an
766 Additional kwargs will be passed to the image creation as additional
767 metadata for the image and will have all values converted to string
768 except for min_disk, min_ram, size and virtual_size which will be
770 If you are sure you have all of your data types correct or have an
771 advanced need to be explicit, use meta. If you are just a normal
772 consumer, using kwargs is likely the right choice.
773 If a value is in meta and kwargs, meta wins.
777 image_id = shade_client.get_image_id(name)
778 if image_id is not None:
779 log.info("Image %s already exists.", name)
781 log.info("Creating image '%s'", name)
782 image = shade_client.create_image(
783 name, filename=filename, container=container, md5=md5, sha256=sha256,
784 disk_format=disk_format, container_format=container_format,
785 disable_vendor_agent=disable_vendor_agent, wait=wait, timeout=timeout,
786 allow_duplicates=allow_duplicates, meta=meta, volume=volume, **kwargs)
787 image_id = image["id"]
789 except exc.OpenStackCloudException as op_exc:
790 log.error("Failed to create_image(shade_client). "
791 "Exception message: %s", op_exc.orig_message)
794 def delete_image(shade_client, name_or_id, wait=False, timeout=3600,
795 delete_objects=True):
797 return shade_client.delete_image(name_or_id, wait=wait,
799 delete_objects=delete_objects)
801 except exc.OpenStackCloudException as op_exc:
802 log.error("Failed to delete_image(shade_client). "
803 "Exception message: %s", op_exc.orig_message)
807 def list_images(shade_client=None):
808 if shade_client is None:
809 shade_client = get_shade_client()
812 return shade_client.list_images()
813 except exc.OpenStackCloudException as o_exc:
814 log.error("Error [list_images(shade_client)]."
815 "Exception message, '%s'", o_exc.orig_message)
819 # *********************************************
821 # *********************************************
822 def get_volume_id(shade_client, volume_name):
823 return shade_client.get_volume_id(volume_name)
826 def get_volume(shade_client, name_or_id, filters=None):
827 """Get a volume by name or ID.
829 :param name_or_id: Name or ID of the volume.
830 :param filters: A dictionary of meta data to use for further filtering.
832 :returns: A volume ``munch.Munch`` or None if no matching volume is found.
834 return shade_client.get_volume(name_or_id, filters=filters)
837 def create_volume(shade_client, size, wait=True, timeout=None,
838 image=None, **kwargs):
841 :param size: Size, in GB of the volume to create.
842 :param name: (optional) Name for the volume.
843 :param description: (optional) Name for the volume.
844 :param wait: If true, waits for volume to be created.
845 :param timeout: Seconds to wait for volume creation. None is forever.
846 :param image: (optional) Image name, ID or object from which to create
849 :returns: The created volume object.
853 return shade_client.create_volume(size, wait=wait, timeout=timeout,
854 image=image, **kwargs)
855 except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as op_exc:
856 log.error("Failed to create_volume(shade_client). "
857 "Exception message: %s", op_exc.orig_message)
860 def delete_volume(shade_client, name_or_id=None, wait=True, timeout=None):
863 :param name_or_id:(string) Name or unique ID of the volume.
864 :param wait:(bool) If true, waits for volume to be deleted.
865 :param timeout:(string) Seconds to wait for volume deletion. None is forever.
867 :return: True on success, False otherwise.
870 return shade_client.delete_volume(name_or_id=name_or_id,
871 wait=wait, timeout=timeout)
872 except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as o_exc:
873 log.error("Error [delete_volume(shade_client,'%s')]. "
874 "Exception message: %s", name_or_id, o_exc.orig_message)
878 def detach_volume(shade_client, server_name_or_id, volume_name_or_id,
879 wait=True, timeout=None):
880 """Detach a volume from a server.
882 :param server_name_or_id: The server name or id to detach from.
883 :param volume_name_or_id: The volume name or id to detach.
884 :param wait: If true, waits for volume to be detached.
885 :param timeout: Seconds to wait for volume detachment. None is forever.
887 :return: True on success.
890 volume = shade_client.get_volume(volume_name_or_id)
891 server = get_server(shade_client, name_or_id=server_name_or_id)
892 shade_client.detach_volume(server, volume, wait=wait, timeout=timeout)
894 except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as o_exc:
895 log.error("Error [detach_volume(shade_client)]. "
896 "Exception message: %s", o_exc.orig_message)