1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
15 from keystoneauth1 import loading
16 from keystoneauth1 import session
20 from cinderclient import client as cinderclient
21 from novaclient import client as novaclient
22 from glanceclient import client as glanceclient
23 from neutronclient.neutron import client as neutronclient
26 log = logging.getLogger(__name__)
28 DEFAULT_HEAT_API_VERSION = '1'
29 DEFAULT_API_VERSION = '2'
32 # *********************************************
34 # *********************************************
35 def get_credentials():
36 """Returns a creds dictionary filled with parsed from env
38 Keystone API version used is 3; v2 was deprecated in 2014 (Icehouse). Along
39 with this deprecation, environment variable 'OS_TENANT_NAME' is replaced by
42 creds = {'username': os.environ.get('OS_USERNAME'),
43 'password': os.environ.get('OS_PASSWORD'),
44 'auth_url': os.environ.get('OS_AUTH_URL'),
45 'project_name': os.environ.get('OS_PROJECT_NAME')
48 if os.getenv('OS_USER_DOMAIN_NAME'):
49 creds['user_domain_name'] = os.getenv('OS_USER_DOMAIN_NAME')
50 if os.getenv('OS_PROJECT_DOMAIN_NAME'):
51 creds['project_domain_name'] = os.getenv('OS_PROJECT_DOMAIN_NAME')
56 def get_session_auth():
57 loader = loading.get_plugin_loader('password')
58 creds = get_credentials()
59 auth = loader.load_from_options(**creds)
64 auth = get_session_auth()
66 cacert = os.environ['OS_CACERT']
68 return session.Session(auth=auth)
70 insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
71 cacert = False if insecure else cacert
72 return session.Session(auth=auth, verify=cacert)
75 def get_endpoint(service_type, endpoint_type='publicURL'):
76 auth = get_session_auth()
77 # for multi-region, we need to specify region
78 # when finding the endpoint
79 return get_session().get_endpoint(auth=auth,
80 service_type=service_type,
81 endpoint_type=endpoint_type,
82 region_name=os.environ.get(
86 # *********************************************
88 # *********************************************
89 def get_heat_api_version(): # pragma: no cover
91 api_version = os.environ['HEAT_API_VERSION']
93 return DEFAULT_HEAT_API_VERSION
95 log.info("HEAT_API_VERSION is set in env as '%s'", api_version)
99 def get_cinder_client_version(): # pragma: no cover
101 api_version = os.environ['OS_VOLUME_API_VERSION']
103 return DEFAULT_API_VERSION
105 log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
109 def get_cinder_client(): # pragma: no cover
111 return cinderclient.Client(get_cinder_client_version(), session=sess)
114 def get_nova_client_version(): # pragma: no cover
116 api_version = os.environ['OS_COMPUTE_API_VERSION']
118 return DEFAULT_API_VERSION
120 log.info("OS_COMPUTE_API_VERSION is set in env as '%s'", api_version)
124 def get_nova_client(): # pragma: no cover
126 return novaclient.Client(get_nova_client_version(), session=sess)
129 def get_neutron_client_version(): # pragma: no cover
131 api_version = os.environ['OS_NETWORK_API_VERSION']
133 return DEFAULT_API_VERSION
135 log.info("OS_NETWORK_API_VERSION is set in env as '%s'", api_version)
139 def get_neutron_client(): # pragma: no cover
141 return neutronclient.Client(get_neutron_client_version(), session=sess)
144 def get_glance_client_version(): # pragma: no cover
146 api_version = os.environ['OS_IMAGE_API_VERSION']
148 return DEFAULT_API_VERSION
150 log.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
154 def get_glance_client(): # pragma: no cover
156 return glanceclient.Client(get_glance_client_version(), session=sess)
159 def get_shade_client():
160 return shade.openstack_cloud()
163 # *********************************************
165 # *********************************************
166 def get_aggregates(nova_client): # pragma: no cover
168 return nova_client.aggregates.list()
169 except Exception: # pylint: disable=broad-except
170 log.exception("Error [get_aggregates(nova_client)]")
173 def get_availability_zones(nova_client): # pragma: no cover
175 return nova_client.availability_zones.list()
176 except Exception: # pylint: disable=broad-except
177 log.exception("Error [get_availability_zones(nova_client)]")
180 def get_availability_zone_names(nova_client): # pragma: no cover
182 return [az.zoneName for az in get_availability_zones(nova_client)]
183 except Exception: # pylint: disable=broad-except
184 log.exception("Error [get_availability_zone_names(nova_client)]")
187 def create_aggregate(nova_client, aggregate_name, av_zone): # pragma: no cover
189 nova_client.aggregates.create(aggregate_name, av_zone)
190 except Exception: # pylint: disable=broad-except
191 log.exception("Error [create_aggregate(nova_client, %s, %s)]",
192 aggregate_name, av_zone)
198 def get_aggregate_id(nova_client, aggregate_name): # pragma: no cover
200 aggregates = get_aggregates(nova_client)
201 _id = next((ag.id for ag in aggregates if ag.name == aggregate_name))
202 except Exception: # pylint: disable=broad-except
203 log.exception("Error [get_aggregate_id(nova_client, %s)]",
209 def add_host_to_aggregate(nova_client, aggregate_name,
210 compute_host): # pragma: no cover
212 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
213 nova_client.aggregates.add_host(aggregate_id, compute_host)
214 except Exception: # pylint: disable=broad-except
215 log.exception("Error [add_host_to_aggregate(nova_client, %s, %s)]",
216 aggregate_name, compute_host)
222 def create_aggregate_with_host(nova_client, aggregate_name, av_zone,
223 compute_host): # pragma: no cover
225 create_aggregate(nova_client, aggregate_name, av_zone)
226 add_host_to_aggregate(nova_client, aggregate_name, compute_host)
227 except Exception: # pylint: disable=broad-except
228 log.exception("Error [create_aggregate_with_host("
229 "nova_client, %s, %s, %s)]",
230 aggregate_name, av_zone, compute_host)
236 def create_keypair(name, key_path=None): # pragma: no cover
238 with open(key_path) as fpubkey:
239 keypair = get_nova_client().keypairs.create(
240 name=name, public_key=fpubkey.read())
242 except Exception: # pylint: disable=broad-except
243 log.exception("Error [create_keypair(nova_client)]")
246 def create_instance(json_body): # pragma: no cover
248 return get_nova_client().servers.create(**json_body)
249 except Exception: # pylint: disable=broad-except
250 log.exception("Error create instance failed")
254 def create_instance_and_wait_for_active(shade_client, name, image,
255 flavor, auto_ip=True, ips=None,
256 ip_pool=None, root_volume=None,
257 terminate_volume=False, wait=True,
258 timeout=180, reuse_ips=True,
259 network=None, boot_from_volume=False,
260 volume_size='20', boot_volume=None,
261 volumes=None, nat_destination=None,
263 """Create a virtual server instance.
265 :param name:(string) Name of the server.
266 :param image:(dict) Image dict, name or ID to boot with. Image is required
267 unless boot_volume is given.
268 :param flavor:(dict) Flavor dict, name or ID to boot onto.
269 :param auto_ip: Whether to take actions to find a routable IP for
271 :param ips: List of IPs to attach to the server.
272 :param ip_pool:(string) Name of the network or floating IP pool to get an
274 :param root_volume:(string) Name or ID of a volume to boot from.
275 (defaults to None - deprecated, use boot_volume)
276 :param boot_volume:(string) Name or ID of a volume to boot from.
277 :param terminate_volume:(bool) If booting from a volume, whether it should
278 be deleted when the server is destroyed.
279 :param volumes:(optional) A list of volumes to attach to the server.
280 :param wait:(optional) Wait for the address to appear as assigned to the server.
281 :param timeout: Seconds to wait, defaults to 60.
282 :param reuse_ips:(bool)Whether to attempt to reuse pre-existing
283 floating ips should a floating IP be needed.
284 :param network:(dict) Network dict or name or ID to attach the server to.
285 Mutually exclusive with the nics parameter. Can also be be
286 a list of network names or IDs or network dicts.
287 :param boot_from_volume:(bool) Whether to boot from volume. 'boot_volume'
288 implies True, but boot_from_volume=True with
289 no boot_volume is valid and will create a
290 volume from the image and use that.
291 :param volume_size: When booting an image from volume, how big should
292 the created volume be?
293 :param nat_destination: Which network should a created floating IP
294 be attached to, if it's not possible to infer from
295 the cloud's configuration.
296 :param meta:(optional) A dict of arbitrary key/value metadata to store for
297 this server. Both keys and values must be <=255 characters.
298 :param reservation_id: A UUID for the set of servers being requested.
299 :param min_count:(optional extension) The minimum number of servers to
301 :param max_count:(optional extension) The maximum number of servers to
303 :param security_groups: A list of security group names.
304 :param userdata: User data to pass to be exposed by the metadata server
305 this can be a file type object as well or a string.
306 :param key_name:(optional extension) Name of previously created keypair to
307 inject into the instance.
308 :param availability_zone: Name of the availability zone for instance
310 :param block_device_mapping:(optional) A dict of block device mappings for
312 :param block_device_mapping_v2:(optional) A dict of block device mappings
314 :param nics:(optional extension) An ordered list of nics to be added to
315 this server, with information about connected networks, fixed
317 :param scheduler_hints:(optional extension) Arbitrary key-value pairs
318 specified by the client to help boot an instance.
319 :param config_drive:(optional extension) Value for config drive either
320 boolean, or volume-id.
321 :param disk_config:(optional extension) Control how the disk is partitioned
322 when the server is created. Possible values are 'AUTO'
324 :param admin_pass:(optional extension) Add a user supplied admin password.
326 :returns: The created server.
329 return shade_client.create_server(
330 name, image, flavor, auto_ip=auto_ip, ips=ips, ip_pool=ip_pool,
331 root_volume=root_volume, terminate_volume=terminate_volume,
332 wait=wait, timeout=timeout, reuse_ips=reuse_ips, network=network,
333 boot_from_volume=boot_from_volume, volume_size=volume_size,
334 boot_volume=boot_volume, volumes=volumes,
335 nat_destination=nat_destination, **kwargs)
336 except exc.OpenStackCloudException as o_exc:
337 log.error("Error [create_instance(shade_client)]. "
338 "Exception message, '%s'", o_exc.orig_message)
341 def attach_server_volume(server_id, volume_id,
342 device=None): # pragma: no cover
344 get_nova_client().volumes.create_server_volume(server_id,
346 except Exception: # pylint: disable=broad-except
347 log.exception("Error [attach_server_volume(nova_client, '%s', '%s')]",
348 server_id, volume_id)
354 def delete_instance(nova_client, instance_id): # pragma: no cover
356 nova_client.servers.force_delete(instance_id)
357 except Exception: # pylint: disable=broad-except
358 log.exception("Error [delete_instance(nova_client, '%s')]",
365 def remove_host_from_aggregate(nova_client, aggregate_name,
366 compute_host): # pragma: no cover
368 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
369 nova_client.aggregates.remove_host(aggregate_id, compute_host)
370 except Exception: # pylint: disable=broad-except
371 log.exception("Error remove_host_from_aggregate(nova_client, %s, %s)",
372 aggregate_name, compute_host)
378 def remove_hosts_from_aggregate(nova_client,
379 aggregate_name): # pragma: no cover
380 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
381 hosts = nova_client.aggregates.get(aggregate_id).hosts
383 all(remove_host_from_aggregate(nova_client, aggregate_name, host)
387 def delete_aggregate(nova_client, aggregate_name): # pragma: no cover
389 remove_hosts_from_aggregate(nova_client, aggregate_name)
390 nova_client.aggregates.delete(aggregate_name)
391 except Exception: # pylint: disable=broad-except
392 log.exception("Error [delete_aggregate(nova_client, %s)]",
399 def get_server_by_name(name): # pragma: no cover
401 return get_nova_client().servers.list(search_opts={'name': name})[0]
403 log.exception('Failed to get nova client')
407 def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover
409 return get_nova_client().flavors.create(name, ram, vcpus,
411 except Exception: # pylint: disable=broad-except
412 log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]",
413 name, ram, disk, vcpus, kwargs['is_public'])
417 def get_image_by_name(name): # pragma: no cover
418 images = get_nova_client().images.list()
420 return next((a for a in images if a.name == name))
421 except StopIteration:
422 log.exception('No image matched')
425 def get_flavor_id(nova_client, flavor_name): # pragma: no cover
426 flavors = nova_client.flavors.list(detailed=True)
429 if f.name == flavor_name:
435 def get_flavor_by_name(name): # pragma: no cover
436 flavors = get_nova_client().flavors.list()
438 return next((a for a in flavors if a.name == name))
439 except StopIteration:
440 log.exception('No flavor matched')
443 def check_status(status, name, iterations, interval): # pragma: no cover
444 for _ in range(iterations):
446 server = get_server_by_name(name)
448 log.error('Cannot found %s server', name)
451 if server.status == status:
458 def delete_flavor(flavor_id): # pragma: no cover
460 get_nova_client().flavors.delete(flavor_id)
461 except Exception: # pylint: disable=broad-except
462 log.exception("Error [delete_flavor(nova_client, %s)]", flavor_id)
468 def delete_keypair(nova_client, key): # pragma: no cover
470 nova_client.keypairs.delete(key=key)
472 except Exception: # pylint: disable=broad-except
473 log.exception("Error [delete_keypair(nova_client)]")
477 # *********************************************
479 # *********************************************
480 def create_neutron_net(shade_client, network_name, shared=False,
481 admin_state_up=True, external=False, provider=None,
483 """Create a neutron network.
485 :param network_name:(string) name of the network being created.
486 :param shared:(bool) whether the network is shared.
487 :param admin_state_up:(bool) set the network administrative state.
488 :param external:(bool) whether this network is externally accessible.
489 :param provider:(dict) a dict of network provider options.
490 :param project_id:(string) specify the project ID this network
491 will be created on (admin-only).
492 :returns:(string) the network id.
495 networks = shade_client.create_network(
496 name=network_name, shared=shared, admin_state_up=admin_state_up,
497 external=external, provider=provider, project_id=project_id)
498 return networks['id']
499 except exc.OpenStackCloudException as o_exc:
500 log.error("Error [create_neutron_net(shade_client)]."
501 "Exception message, '%s'", o_exc.orig_message)
505 def delete_neutron_net(shade_client, network_id):
507 return shade_client.delete_network(network_id)
508 except exc.OpenStackCloudException:
509 log.error("Error [delete_neutron_net(shade_client, '%s')]", network_id)
513 def create_neutron_subnet(shade_client, network_name_or_id, cidr=None,
514 ip_version=4, enable_dhcp=False, subnet_name=None,
515 tenant_id=None, allocation_pools=None,
516 gateway_ip=None, disable_gateway_ip=False,
517 dns_nameservers=None, host_routes=None,
518 ipv6_ra_mode=None, ipv6_address_mode=None,
519 use_default_subnetpool=False):
520 """Create a subnet on a specified network.
522 :param network_name_or_id:(string) the unique name or ID of the
523 attached network. If a non-unique name is
524 supplied, an exception is raised.
525 :param cidr:(string) the CIDR.
526 :param ip_version:(int) the IP version.
527 :param enable_dhcp:(bool) whether DHCP is enable.
528 :param subnet_name:(string) the name of the subnet.
529 :param tenant_id:(string) the ID of the tenant who owns the network.
530 :param allocation_pools: A list of dictionaries of the start and end
531 addresses for the allocation pools.
532 :param gateway_ip:(string) the gateway IP address.
533 :param disable_gateway_ip:(bool) whether gateway IP address is enabled.
534 :param dns_nameservers: A list of DNS name servers for the subnet.
535 :param host_routes: A list of host route dictionaries for the subnet.
536 :param ipv6_ra_mode:(string) IPv6 Router Advertisement mode.
537 Valid values are: 'dhcpv6-stateful',
538 'dhcpv6-stateless', or 'slaac'.
539 :param ipv6_address_mode:(string) IPv6 address mode.
540 Valid values are: 'dhcpv6-stateful',
541 'dhcpv6-stateless', or 'slaac'.
542 :param use_default_subnetpool:(bool) use the default subnetpool for
543 ``ip_version`` to obtain a CIDR. It is
544 required to pass ``None`` to the ``cidr``
545 argument when enabling this option.
546 :returns:(string) the subnet id.
549 subnet = shade_client.create_subnet(
550 network_name_or_id, cidr=cidr, ip_version=ip_version,
551 enable_dhcp=enable_dhcp, subnet_name=subnet_name,
552 tenant_id=tenant_id, allocation_pools=allocation_pools,
553 gateway_ip=gateway_ip, disable_gateway_ip=disable_gateway_ip,
554 dns_nameservers=dns_nameservers, host_routes=host_routes,
555 ipv6_ra_mode=ipv6_ra_mode, ipv6_address_mode=ipv6_address_mode,
556 use_default_subnetpool=use_default_subnetpool)
558 except exc.OpenStackCloudException as o_exc:
559 log.error("Error [create_neutron_subnet(shade_client)]. "
560 "Exception message: %s", o_exc.orig_message)
564 def create_neutron_router(shade_client, name=None, admin_state_up=True,
565 ext_gateway_net_id=None, enable_snat=None,
566 ext_fixed_ips=None, project_id=None):
567 """Create a logical router.
569 :param name:(string) the router name.
570 :param admin_state_up:(bool) the administrative state of the router.
571 :param ext_gateway_net_id:(string) network ID for the external gateway.
572 :param enable_snat:(bool) enable Source NAT (SNAT) attribute.
573 :param ext_fixed_ips: List of dictionaries of desired IP and/or subnet
574 on the external network.
575 :param project_id:(string) project ID for the router.
577 :returns:(string) the router id.
580 router = shade_client.create_router(
581 name, admin_state_up, ext_gateway_net_id, enable_snat,
582 ext_fixed_ips, project_id)
584 except exc.OpenStackCloudException as o_exc:
585 log.error("Error [create_neutron_router(shade_client)]. "
586 "Exception message: %s", o_exc.orig_message)
589 def delete_neutron_router(shade_client, router_id):
591 return shade_client.delete_router(router_id)
592 except exc.OpenStackCloudException as o_exc:
593 log.error("Error [delete_neutron_router(shade_client, '%s')]. "
594 "Exception message: %s", router_id, o_exc.orig_message)
598 def remove_gateway_router(neutron_client, router_id): # pragma: no cover
600 neutron_client.remove_gateway_router(router_id)
602 except Exception: # pylint: disable=broad-except
603 log.error("Error [remove_gateway_router(neutron_client, '%s')]",
608 def remove_router_interface(shade_client, router, subnet_id=None,
610 """Detach a subnet from an internal router interface.
612 At least one of subnet_id or port_id must be supplied. If you specify both
613 subnet and port ID, the subnet ID must correspond to the subnet ID of the
614 first IP address on the port specified by the port ID.
615 Otherwise an error occurs.
617 :param router: The dict object of the router being changed
618 :param subnet_id:(string) The ID of the subnet to use for the interface
619 :param port_id:(string) The ID of the port to use for the interface
620 :returns: True on success
623 shade_client.remove_router_interface(
624 router, subnet_id=subnet_id, port_id=port_id)
626 except exc.OpenStackCloudException as o_exc:
627 log.error("Error [remove_interface_router(shade_client)]. "
628 "Exception message: %s", o_exc.orig_message)
632 def create_floating_ip(shade_client, network_name_or_id=None, server=None,
633 fixed_address=None, nat_destination=None,
634 port=None, wait=False, timeout=60):
635 """Allocate a new floating IP from a network or a pool.
637 :param network_name_or_id: Name or ID of the network
638 that the floating IP should come from.
639 :param server: Server dict for the server to create
640 the IP for and to which it should be attached.
641 :param fixed_address: Fixed IP to attach the floating ip to.
642 :param nat_destination: Name or ID of the network
643 that the fixed IP to attach the floating
645 :param port: The port ID that the floating IP should be
646 attached to. Specifying a port conflicts with specifying a
647 server,fixed_address or nat_destination.
648 :param wait: Whether to wait for the IP to be active.Only applies
649 if a server is provided.
650 :param timeout: How long to wait for the IP to be active.Only
651 applies if a server is provided.
653 :returns:Floating IP id and address
656 fip = shade_client.create_floating_ip(
657 network=network_name_or_id, server=server,
658 fixed_address=fixed_address, nat_destination=nat_destination,
659 port=port, wait=wait, timeout=timeout)
660 return {'fip_addr': fip['floating_ip_address'], 'fip_id': fip['id']}
661 except exc.OpenStackCloudException as o_exc:
662 log.error("Error [create_floating_ip(shade_client)]. "
663 "Exception message: %s", o_exc.orig_message)
666 def delete_floating_ip(shade_client, floating_ip_id, retry=1):
668 return shade_client.delete_floating_ip(floating_ip_id=floating_ip_id,
670 except exc.OpenStackCloudException as o_exc:
671 log.error("Error [delete_floating_ip(shade_client,'%s')]. "
672 "Exception message: %s", floating_ip_id, o_exc.orig_message)
676 def create_security_group_rule(shade_client, secgroup_name_or_id,
677 port_range_min=None, port_range_max=None,
678 protocol=None, remote_ip_prefix=None,
679 remote_group_id=None, direction='ingress',
680 ethertype='IPv4', project_id=None):
681 """Create a new security group rule
683 :param secgroup_name_or_id:(string) The security group name or ID to
684 associate with this security group rule. If a
685 non-unique group name is given, an exception is
687 :param port_range_min:(int) The minimum port number in the range that is
688 matched by the security group rule. If the protocol
689 is TCP or UDP, this value must be less than or equal
690 to the port_range_max attribute value. If nova is
691 used by the cloud provider for security groups, then
692 a value of None will be transformed to -1.
693 :param port_range_max:(int) The maximum port number in the range that is
694 matched by the security group rule. The
695 port_range_min attribute constrains the
696 port_range_max attribute. If nova is used by the
697 cloud provider for security groups, then a value of
698 None will be transformed to -1.
699 :param protocol:(string) The protocol that is matched by the security group
700 rule. Valid values are None, tcp, udp, and icmp.
701 :param remote_ip_prefix:(string) The remote IP prefix to be associated with
702 this security group rule. This attribute matches
703 the specified IP prefix as the source IP address of
705 :param remote_group_id:(string) The remote group ID to be associated with
706 this security group rule.
707 :param direction:(string) Ingress or egress: The direction in which the
708 security group rule is applied.
709 :param ethertype:(string) Must be IPv4 or IPv6, and addresses represented
710 in CIDR must match the ingress or egress rules.
711 :param project_id:(string) Specify the project ID this security group will
712 be created on (admin-only).
714 :returns: True on success.
718 shade_client.create_security_group_rule(
719 secgroup_name_or_id, port_range_min=port_range_min,
720 port_range_max=port_range_max, protocol=protocol,
721 remote_ip_prefix=remote_ip_prefix, remote_group_id=remote_group_id,
722 direction=direction, ethertype=ethertype, project_id=project_id)
724 except exc.OpenStackCloudException as op_exc:
725 log.error("Failed to create_security_group_rule(shade_client). "
726 "Exception message: %s", op_exc.orig_message)
730 def create_security_group_full(shade_client, sg_name,
731 sg_description, project_id=None):
732 security_group = shade_client.get_security_group(sg_name)
735 log.info("Using existing security group '%s'...", sg_name)
736 return security_group['id']
738 log.info("Creating security group '%s'...", sg_name)
740 security_group = shade_client.create_security_group(
741 sg_name, sg_description, project_id=project_id)
742 except (exc.OpenStackCloudException,
743 exc.OpenStackCloudUnavailableFeature) as op_exc:
744 log.error("Error [create_security_group(shade_client, %s, %s)]. "
745 "Exception message: %s", sg_name, sg_description,
749 log.debug("Security group '%s' with ID=%s created successfully.",
750 security_group['name'], security_group['id'])
752 log.debug("Adding ICMP rules in security group '%s'...", sg_name)
753 if not create_security_group_rule(shade_client, security_group['id'],
754 direction='ingress', protocol='icmp'):
755 log.error("Failed to create the security group rule...")
756 shade_client.delete_security_group(sg_name)
759 log.debug("Adding SSH rules in security group '%s'...", sg_name)
760 if not create_security_group_rule(shade_client, security_group['id'],
761 direction='ingress', protocol='tcp',
763 port_range_max='22'):
764 log.error("Failed to create the security group rule...")
765 shade_client.delete_security_group(sg_name)
768 if not create_security_group_rule(shade_client, security_group['id'],
769 direction='egress', protocol='tcp',
771 port_range_max='22'):
772 log.error("Failed to create the security group rule...")
773 shade_client.delete_security_group(sg_name)
775 return security_group['id']
778 # *********************************************
780 # *********************************************
781 def get_image_id(glance_client, image_name): # pragma: no cover
782 images = glance_client.images.list()
783 return next((i.id for i in images if i.name == image_name), None)
786 def create_image(glance_client, image_name, file_path, disk_format,
787 container_format, min_disk, min_ram, protected, tag,
788 public, **kwargs): # pragma: no cover
789 if not os.path.isfile(file_path):
790 log.error("Error: file %s does not exist.", file_path)
793 image_id = get_image_id(glance_client, image_name)
794 if image_id is not None:
795 log.info("Image %s already exists.", image_name)
797 log.info("Creating image '%s' from '%s'...", image_name, file_path)
799 image = glance_client.images.create(
800 name=image_name, visibility=public, disk_format=disk_format,
801 container_format=container_format, min_disk=min_disk,
802 min_ram=min_ram, tags=tag, protected=protected, **kwargs)
804 with open(file_path) as image_data:
805 glance_client.images.upload(image_id, image_data)
807 except Exception: # pylint: disable=broad-except
809 "Error [create_glance_image(glance_client, '%s', '%s', '%s')]",
810 image_name, file_path, public)
814 def delete_image(glance_client, image_id): # pragma: no cover
816 glance_client.images.delete(image_id)
818 except Exception: # pylint: disable=broad-except
819 log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
825 def list_images(shade_client=None):
826 if shade_client is None:
827 shade_client = get_shade_client()
830 return shade_client.list_images()
831 except exc.OpenStackCloudException as o_exc:
832 log.error("Error [list_images(shade_client)]."
833 "Exception message, '%s'", o_exc.orig_message)
837 # *********************************************
839 # *********************************************
840 def get_volume_id(volume_name): # pragma: no cover
841 volumes = get_cinder_client().volumes.list()
842 return next((v.id for v in volumes if v.name == volume_name), None)
845 def create_volume(cinder_client, volume_name, volume_size,
846 volume_image=False): # pragma: no cover
849 volume = cinder_client.volumes.create(name=volume_name,
851 imageRef=volume_image)
853 volume = cinder_client.volumes.create(name=volume_name,
856 except Exception: # pylint: disable=broad-except
857 log.exception("Error [create_volume(cinder_client, %s)]",
858 (volume_name, volume_size))
862 def delete_volume(cinder_client, volume_id,
863 forced=False): # pragma: no cover
867 cinder_client.volumes.detach(volume_id)
868 except Exception: # pylint: disable=broad-except
869 log.error(sys.exc_info()[0])
870 cinder_client.volumes.force_delete(volume_id)
873 volume = get_cinder_client().volumes.get(volume_id)
874 if volume.status.lower() == 'available':
876 cinder_client.volumes.delete(volume_id)
878 except Exception: # pylint: disable=broad-except
879 log.exception("Error [delete_volume(cinder_client, '%s')]", volume_id)
883 def detach_volume(server_id, volume_id): # pragma: no cover
885 get_nova_client().volumes.delete_server_volume(server_id, volume_id)
887 except Exception: # pylint: disable=broad-except
888 log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]",
889 server_id, volume_id)