Replace nova attach volume to server with shade client.
[yardstick.git] / yardstick / common / openstack_utils.py
index a4fd4e5..0a3cfb5 100644 (file)
@@ -8,7 +8,6 @@
 ##############################################################################
 
 import os
-import time
 import sys
 import logging
 
@@ -163,189 +162,163 @@ def get_shade_client():
 # *********************************************
 #   NOVA
 # *********************************************
-def get_instances(nova_client):
-    try:
-        return nova_client.servers.list(search_opts={'all_tenants': 1})
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_instances(nova_client)]")
-
-
-def get_instance_status(nova_client, instance):     # pragma: no cover
-    try:
-        return nova_client.servers.get(instance.id).status
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_instance_status(nova_client)]")
-
-
-def get_instance_by_name(nova_client, instance_name):   # pragma: no cover
-    try:
-        return nova_client.servers.find(name=instance_name)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_instance_by_name(nova_client, '%s')]",
-                      instance_name)
-
-
-def get_aggregates(nova_client):    # pragma: no cover
-    try:
-        return nova_client.aggregates.list()
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_aggregates(nova_client)]")
-
-
-def get_availability_zones(nova_client):    # pragma: no cover
-    try:
-        return nova_client.availability_zones.list()
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_availability_zones(nova_client)]")
-
-
-def get_availability_zone_names(nova_client):   # pragma: no cover
-    try:
-        return [az.zoneName for az in get_availability_zones(nova_client)]
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_availability_zone_names(nova_client)]")
-
-
-def create_aggregate(nova_client, aggregate_name, av_zone):  # pragma: no cover
-    try:
-        nova_client.aggregates.create(aggregate_name, av_zone)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [create_aggregate(nova_client, %s, %s)]",
-                      aggregate_name, av_zone)
-        return False
-    else:
-        return True
-
-
-def get_aggregate_id(nova_client, aggregate_name):      # pragma: no cover
-    try:
-        aggregates = get_aggregates(nova_client)
-        _id = next((ag.id for ag in aggregates if ag.name == aggregate_name))
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_aggregate_id(nova_client, %s)]",
-                      aggregate_name)
-    else:
-        return _id
+def create_keypair(shade_client, name, public_key=None):
+    """Create a new keypair.
 
+    :param name: Name of the keypair being created.
+    :param public_key: Public key for the new keypair.
 
-def add_host_to_aggregate(nova_client, aggregate_name,
-                          compute_host):    # pragma: no cover
-    try:
-        aggregate_id = get_aggregate_id(nova_client, aggregate_name)
-        nova_client.aggregates.add_host(aggregate_id, compute_host)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [add_host_to_aggregate(nova_client, %s, %s)]",
-                      aggregate_name, compute_host)
-        return False
-    else:
-        return True
-
-
-def create_aggregate_with_host(nova_client, aggregate_name, av_zone,
-                               compute_host):    # pragma: no cover
+    :return: Created keypair.
+    """
     try:
-        create_aggregate(nova_client, aggregate_name, av_zone)
-        add_host_to_aggregate(nova_client, aggregate_name, compute_host)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [create_aggregate_with_host("
-                      "nova_client, %s, %s, %s)]",
-                      aggregate_name, av_zone, compute_host)
-        return False
-    else:
-        return True
+        return shade_client.create_keypair(name, public_key=public_key)
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [create_keypair(shade_client)]. "
+                  "Exception message, '%s'", o_exc.orig_message)
 
 
-def create_keypair(name, key_path=None):    # pragma: no cover
+def create_instance_and_wait_for_active(shade_client, name, image,
+                                        flavor, auto_ip=True, ips=None,
+                                        ip_pool=None, root_volume=None,
+                                        terminate_volume=False, wait=True,
+                                        timeout=180, reuse_ips=True,
+                                        network=None, boot_from_volume=False,
+                                        volume_size='20', boot_volume=None,
+                                        volumes=None, nat_destination=None,
+                                        **kwargs):
+    """Create a virtual server instance.
+
+    :param name:(string) Name of the server.
+    :param image:(dict) Image dict, name or ID to boot with. Image is required
+                 unless boot_volume is given.
+    :param flavor:(dict) Flavor dict, name or ID to boot onto.
+    :param auto_ip: Whether to take actions to find a routable IP for
+                    the server.
+    :param ips: List of IPs to attach to the server.
+    :param ip_pool:(string) Name of the network or floating IP pool to get an
+                   address from.
+    :param root_volume:(string) Name or ID of a volume to boot from.
+                       (defaults to None - deprecated, use boot_volume)
+    :param boot_volume:(string) Name or ID of a volume to boot from.
+    :param terminate_volume:(bool) If booting from a volume, whether it should
+                            be deleted when the server is destroyed.
+    :param volumes:(optional) A list of volumes to attach to the server.
+    :param wait:(optional) Wait for the address to appear as assigned to the server.
+    :param timeout: Seconds to wait, defaults to 60.
+    :param reuse_ips:(bool)Whether to attempt to reuse pre-existing
+                     floating ips should a floating IP be needed.
+    :param network:(dict) Network dict or name or ID to attach the server to.
+                   Mutually exclusive with the nics parameter. Can also be be
+                   a list of network names or IDs or network dicts.
+    :param boot_from_volume:(bool) Whether to boot from volume. 'boot_volume'
+                            implies True, but boot_from_volume=True with
+                            no boot_volume is valid and will create a
+                            volume from the image and use that.
+    :param volume_size: When booting an image from volume, how big should
+                        the created volume be?
+    :param nat_destination: Which network should a created floating IP
+                            be attached to, if it's not possible to infer from
+                            the cloud's configuration.
+    :param meta:(optional) A dict of arbitrary key/value metadata to store for
+                this server. Both keys and values must be <=255 characters.
+    :param reservation_id: A UUID for the set of servers being requested.
+    :param min_count:(optional extension) The minimum number of servers to
+                     launch.
+    :param max_count:(optional extension) The maximum number of servers to
+                     launch.
+    :param security_groups: A list of security group names.
+    :param userdata: User data to pass to be exposed by the metadata server
+                     this can be a file type object as well or a string.
+    :param key_name:(optional extension) Name of previously created keypair to
+                    inject into the instance.
+    :param availability_zone: Name of the availability zone for instance
+                              placement.
+    :param block_device_mapping:(optional) A dict of block device mappings for
+                                this server.
+    :param block_device_mapping_v2:(optional) A dict of block device mappings
+                                   for this server.
+    :param nics:(optional extension) An ordered list of nics to be added to
+                 this server, with information about connected networks, fixed
+                 IPs, port etc.
+    :param scheduler_hints:(optional extension) Arbitrary key-value pairs
+                           specified by the client to help boot an instance.
+    :param config_drive:(optional extension) Value for config drive either
+                         boolean, or volume-id.
+    :param disk_config:(optional extension) Control how the disk is partitioned
+                       when the server is created. Possible values are 'AUTO'
+                       or 'MANUAL'.
+    :param admin_pass:(optional extension) Add a user supplied admin password.
+
+    :returns: The created server.
+    """
     try:
-        with open(key_path) as fpubkey:
-            keypair = get_nova_client().keypairs.create(
-                name=name, public_key=fpubkey.read())
-            return keypair
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [create_keypair(nova_client)]")
+        return shade_client.create_server(
+            name, image, flavor, auto_ip=auto_ip, ips=ips, ip_pool=ip_pool,
+            root_volume=root_volume, terminate_volume=terminate_volume,
+            wait=wait, timeout=timeout, reuse_ips=reuse_ips, network=network,
+            boot_from_volume=boot_from_volume, volume_size=volume_size,
+            boot_volume=boot_volume, volumes=volumes,
+            nat_destination=nat_destination, **kwargs)
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [create_instance(shade_client)]. "
+                  "Exception message, '%s'", o_exc.orig_message)
 
 
-def create_instance(json_body):    # pragma: no cover
-    try:
-        return get_nova_client().servers.create(**json_body)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error create instance failed")
-        return None
+def attach_volume_to_server(shade_client, server_name_or_id, volume_name_or_id,
+                            device=None, wait=True, timeout=None):
+    """Attach a volume to a server.
 
+    This will attach a volume, described by the passed in volume
+    dict, to the server described by the passed in server dict on the named
+    device on the server.
 
-def create_instance_and_wait_for_active(json_body):    # pragma: no cover
-    SLEEP = 3
-    VM_BOOT_TIMEOUT = 180
-    nova_client = get_nova_client()
-    instance = create_instance(json_body)
-    for _ in range(int(VM_BOOT_TIMEOUT / SLEEP)):
-        status = get_instance_status(nova_client, instance)
-        if status.lower() == "active":
-            return instance
-        elif status.lower() == "error":
-            log.error("The instance went to ERROR status.")
-            return None
-        time.sleep(SLEEP)
-    log.error("Timeout booting the instance.")
-    return None
-
-
-def attach_server_volume(server_id, volume_id,
-                         device=None):    # pragma: no cover
-    try:
-        get_nova_client().volumes.create_server_volume(server_id,
-                                                       volume_id, device)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [attach_server_volume(nova_client, '%s', '%s')]",
-                      server_id, volume_id)
-        return False
-    else:
-        return True
+    If the volume is already attached to the server, or generally not
+    available, then an exception is raised. To re-attach to a server,
+    but under a different device, the user must detach it first.
 
+    :param server_name_or_id:(string) The server name or id to attach to.
+    :param volume_name_or_id:(string) The volume name or id to attach.
+    :param device:(string) The device name where the volume will attach.
+    :param wait:(bool) If true, waits for volume to be attached.
+    :param timeout: Seconds to wait for volume attachment. None is forever.
 
-def delete_instance(nova_client, instance_id):      # pragma: no cover
+    :returns: True if attached successful, False otherwise.
+    """
     try:
-        nova_client.servers.force_delete(instance_id)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [delete_instance(nova_client, '%s')]",
-                      instance_id)
-        return False
-    else:
+        server = shade_client.get_server(name_or_id=server_name_or_id)
+        volume = shade_client.get_volume(volume_name_or_id)
+        shade_client.attach_volume(
+            server, volume, device=device, wait=wait, timeout=timeout)
         return True
-
-
-def remove_host_from_aggregate(nova_client, aggregate_name,
-                               compute_host):  # pragma: no cover
-    try:
-        aggregate_id = get_aggregate_id(nova_client, aggregate_name)
-        nova_client.aggregates.remove_host(aggregate_id, compute_host)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error remove_host_from_aggregate(nova_client, %s, %s)",
-                      aggregate_name, compute_host)
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [attach_volume_to_server(shade_client)]. "
+                  "Exception message: %s", o_exc.orig_message)
         return False
-    else:
-        return True
 
 
-def remove_hosts_from_aggregate(nova_client,
-                                aggregate_name):   # pragma: no cover
-    aggregate_id = get_aggregate_id(nova_client, aggregate_name)
-    hosts = nova_client.aggregates.get(aggregate_id).hosts
-    assert(
-        all(remove_host_from_aggregate(nova_client, aggregate_name, host)
-            for host in hosts))
+def delete_instance(shade_client, name_or_id, wait=False, timeout=180,
+                    delete_ips=False, delete_ip_retry=1):
+    """Delete a server instance.
 
-
-def delete_aggregate(nova_client, aggregate_name):  # pragma: no cover
+    :param name_or_id: name or ID of the server to delete
+    :param wait:(bool) If true, waits for server to be deleted.
+    :param timeout:(int) Seconds to wait for server deletion.
+    :param delete_ips:(bool) If true, deletes any floating IPs associated with
+                      the instance.
+    :param delete_ip_retry:(int) Number of times to retry deleting
+                           any floating ips, should the first try be
+                           unsuccessful.
+    :returns: True if delete succeeded, False otherwise.
+    """
     try:
-        remove_hosts_from_aggregate(nova_client, aggregate_name)
-        nova_client.aggregates.delete(aggregate_name)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [delete_aggregate(nova_client, %s)]",
-                      aggregate_name)
+        return shade_client.delete_server(
+            name_or_id, wait=wait, timeout=timeout, delete_ips=delete_ips,
+            delete_ip_retry=delete_ip_retry)
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [delete_instance(shade_client, '%s')]. "
+                  "Exception message: %s", name_or_id,
+                  o_exc.orig_message)
         return False
-    else:
-        return True
 
 
 def get_server_by_name(name):   # pragma: no cover
@@ -366,14 +339,6 @@ def create_flavor(name, ram, vcpus, disk, **kwargs):   # pragma: no cover
         return None
 
 
-def get_image_by_name(name):    # pragma: no cover
-    images = get_nova_client().images.list()
-    try:
-        return next((a for a in images if a.name == name))
-    except StopIteration:
-        log.exception('No image matched')
-
-
 def get_flavor_id(nova_client, flavor_name):    # pragma: no cover
     flavors = nova_client.flavors.list(detailed=True)
     flavor_id = ''
@@ -392,21 +357,6 @@ def get_flavor_by_name(name):   # pragma: no cover
         log.exception('No flavor matched')
 
 
-def check_status(status, name, iterations, interval):   # pragma: no cover
-    for _ in range(iterations):
-        try:
-            server = get_server_by_name(name)
-        except IndexError:
-            log.error('Cannot found %s server', name)
-            raise
-
-        if server.status == status:
-            return True
-
-        time.sleep(interval)
-    return False
-
-
 def delete_flavor(flavor_id):    # pragma: no cover
     try:
         get_nova_client().flavors.delete(flavor_id)
@@ -417,24 +367,24 @@ def delete_flavor(flavor_id):    # pragma: no cover
         return True
 
 
-def delete_keypair(nova_client, key):     # pragma: no cover
+def delete_keypair(shade_client, name):
+    """Delete a keypair.
+
+    :param name: Name of the keypair to delete.
+
+    :returns: True if delete succeeded, False otherwise.
+    """
     try:
-        nova_client.keypairs.delete(key=key)
-        return True
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [delete_keypair(nova_client)]")
+        return shade_client.delete_keypair(name)
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [delete_neutron_router(shade_client, '%s')]. "
+                  "Exception message: %s", name, o_exc.orig_message)
         return False
 
 
 # *********************************************
 #   NEUTRON
 # *********************************************
-def get_network_id(shade_client, network_name):
-    networks = shade_client.list_networks({'name': network_name})
-    if networks:
-        return networks[0]['id']
-
-
 def create_neutron_net(shade_client, network_name, shared=False,
                        admin_state_up=True, external=False, provider=None,
                        project_id=None):
@@ -563,151 +513,174 @@ def remove_gateway_router(neutron_client, router_id):      # pragma: no cover
         return False
 
 
-def remove_interface_router(neutron_client, router_id, subnet_id,
-                            **json_body):      # pragma: no cover
-    json_body.update({"subnet_id": subnet_id})
-    try:
-        neutron_client.remove_interface_router(router=router_id,
-                                               body=json_body)
-        return True
-    except Exception:  # pylint: disable=broad-except
-        log.error("Error [remove_interface_router(neutron_client, '%s', "
-                  "'%s')]", router_id, subnet_id)
-        return False
-
-
-def create_floating_ip(neutron_client, extnet_id):      # pragma: no cover
-    props = {'floating_network_id': extnet_id}
-    try:
-        ip_json = neutron_client.create_floatingip({'floatingip': props})
-        fip_addr = ip_json['floatingip']['floating_ip_address']
-        fip_id = ip_json['floatingip']['id']
-    except Exception:  # pylint: disable=broad-except
-        log.error("Error [create_floating_ip(neutron_client)]")
-        return None
-    return {'fip_addr': fip_addr, 'fip_id': fip_id}
+def remove_router_interface(shade_client, router, subnet_id=None,
+                            port_id=None):
+    """Detach a subnet from an internal router interface.
 
+    At least one of subnet_id or port_id must be supplied. If you specify both
+    subnet and port ID, the subnet ID must correspond to the subnet ID of the
+    first IP address on the port specified by the port ID.
+    Otherwise an error occurs.
 
-def delete_floating_ip(nova_client, floatingip_id):      # pragma: no cover
+    :param router: The dict object of the router being changed
+    :param subnet_id:(string) The ID of the subnet to use for the interface
+    :param port_id:(string) The ID of the port to use for the interface
+    :returns: True on success
+    """
     try:
-        nova_client.floating_ips.delete(floatingip_id)
+        shade_client.remove_router_interface(
+            router, subnet_id=subnet_id, port_id=port_id)
         return True
-    except Exception:  # pylint: disable=broad-except
-        log.error("Error [delete_floating_ip(nova_client, '%s')]",
-                  floatingip_id)
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [remove_interface_router(shade_client)]. "
+                  "Exception message: %s", o_exc.orig_message)
         return False
 
 
-def get_security_groups(neutron_client):      # pragma: no cover
+def create_floating_ip(shade_client, network_name_or_id=None, server=None,
+                       fixed_address=None, nat_destination=None,
+                       port=None, wait=False, timeout=60):
+    """Allocate a new floating IP from a network or a pool.
+
+    :param network_name_or_id: Name or ID of the network
+                               that the floating IP should come from.
+    :param server: Server dict for the server to create
+                  the IP for and to which it should be attached.
+    :param fixed_address: Fixed IP to attach the floating ip to.
+    :param nat_destination: Name or ID of the network
+                           that the fixed IP to attach the floating
+                           IP to should be on.
+    :param port: The port ID that the floating IP should be
+                attached to. Specifying a port conflicts with specifying a
+                server,fixed_address or nat_destination.
+    :param wait: Whether to wait for the IP to be active.Only applies
+                if a server is provided.
+    :param timeout: How long to wait for the IP to be active.Only
+                   applies if a server is provided.
+
+    :returns:Floating IP id and address
+    """
     try:
-        security_groups = neutron_client.list_security_groups()[
-            'security_groups']
-        return security_groups
-    except Exception:  # pylint: disable=broad-except
-        log.error("Error [get_security_groups(neutron_client)]")
-        return None
-
-
-def get_security_group_id(neutron_client, sg_name):      # pragma: no cover
-    security_groups = get_security_groups(neutron_client)
-    id = ''
-    for sg in security_groups:
-        if sg['name'] == sg_name:
-            id = sg['id']
-            break
-    return id
+        fip = shade_client.create_floating_ip(
+            network=network_name_or_id, server=server,
+            fixed_address=fixed_address, nat_destination=nat_destination,
+            port=port, wait=wait, timeout=timeout)
+        return {'fip_addr': fip['floating_ip_address'], 'fip_id': fip['id']}
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [create_floating_ip(shade_client)]. "
+                  "Exception message: %s", o_exc.orig_message)
 
 
-def create_security_group(neutron_client, sg_name,
-                          sg_description):      # pragma: no cover
-    json_body = {'security_group': {'name': sg_name,
-                                    'description': sg_description}}
+def delete_floating_ip(shade_client, floating_ip_id, retry=1):
     try:
-        secgroup = neutron_client.create_security_group(json_body)
-        return secgroup['security_group']
-    except Exception:  # pylint: disable=broad-except
-        log.error("Error [create_security_group(neutron_client, '%s', "
-                  "'%s')]", sg_name, sg_description)
-        return None
+        return shade_client.delete_floating_ip(floating_ip_id=floating_ip_id,
+                                               retry=retry)
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [delete_floating_ip(shade_client,'%s')]. "
+                  "Exception message: %s", floating_ip_id, o_exc.orig_message)
+        return False
 
 
-def create_secgroup_rule(neutron_client, sg_id, direction, protocol,
-                         port_range_min=None, port_range_max=None,
-                         **json_body):      # pragma: no cover
-    # We create a security group in 2 steps
-    # 1 - we check the format and set the json body accordingly
-    # 2 - we call neturon client to create the security group
-
-    # Format check
-    json_body.update({'security_group_rule': {'direction': direction,
-                     'security_group_id': sg_id, 'protocol': protocol}})
-    # parameters may be
-    # - both None => we do nothing
-    # - both Not None => we add them to the json description
-    # but one cannot be None is the other is not None
-    if (port_range_min is not None and port_range_max is not None):
-        # add port_range in json description
-        json_body['security_group_rule']['port_range_min'] = port_range_min
-        json_body['security_group_rule']['port_range_max'] = port_range_max
-        log.debug("Security_group format set (port range included)")
-    else:
-        # either both port range are set to None => do nothing
-        # or one is set but not the other => log it and return False
-        if port_range_min is None and port_range_max is None:
-            log.debug("Security_group format set (no port range mentioned)")
-        else:
-            log.error("Bad security group format."
-                      "One of the port range is not properly set:"
-                      "range min: %s, range max: %s", port_range_min,
-                      port_range_max)
-            return False
+def create_security_group_rule(shade_client, secgroup_name_or_id,
+                               port_range_min=None, port_range_max=None,
+                               protocol=None, remote_ip_prefix=None,
+                               remote_group_id=None, direction='ingress',
+                               ethertype='IPv4', project_id=None):
+    """Create a new security group rule
+
+    :param secgroup_name_or_id:(string) The security group name or ID to
+                               associate with this security group rule. If a
+                               non-unique group name is given, an exception is
+                               raised.
+    :param port_range_min:(int) The minimum port number in the range that is
+                          matched by the security group rule. If the protocol
+                          is TCP or UDP, this value must be less than or equal
+                          to the port_range_max attribute value. If nova is
+                          used by the cloud provider for security groups, then
+                          a value of None will be transformed to -1.
+    :param port_range_max:(int) The maximum port number in the range that is
+                          matched by the security group rule. The
+                          port_range_min attribute constrains the
+                          port_range_max attribute. If nova is used by the
+                          cloud provider for security groups, then a value of
+                          None will be transformed to -1.
+    :param protocol:(string) The protocol that is matched by the security group
+                    rule. Valid values are None, tcp, udp, and icmp.
+    :param remote_ip_prefix:(string) The remote IP prefix to be associated with
+                            this security group rule. This attribute matches
+                            the specified IP prefix as the source IP address of
+                            the IP packet.
+    :param remote_group_id:(string) The remote group ID to be associated with
+                           this security group rule.
+    :param direction:(string) Ingress or egress: The direction in which the
+                     security group rule is applied.
+    :param ethertype:(string) Must be IPv4 or IPv6, and addresses represented
+                     in CIDR must match the ingress or egress rules.
+    :param project_id:(string) Specify the project ID this security group will
+                      be created on (admin-only).
+
+    :returns: True on success.
+    """
 
-    # Create security group using neutron client
     try:
-        neutron_client.create_security_group_rule(json_body)
+        shade_client.create_security_group_rule(
+            secgroup_name_or_id, port_range_min=port_range_min,
+            port_range_max=port_range_max, protocol=protocol,
+            remote_ip_prefix=remote_ip_prefix, remote_group_id=remote_group_id,
+            direction=direction, ethertype=ethertype, project_id=project_id)
         return True
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Impossible to create_security_group_rule,"
-                      "security group rule probably already exists")
+    except exc.OpenStackCloudException as op_exc:
+        log.error("Failed to create_security_group_rule(shade_client). "
+                  "Exception message: %s", op_exc.orig_message)
         return False
 
 
-def create_security_group_full(neutron_client, sg_name,
-                               sg_description):      # pragma: no cover
-    sg_id = get_security_group_id(neutron_client, sg_name)
-    if sg_id != '':
+def create_security_group_full(shade_client, sg_name,
+                               sg_description, project_id=None):
+    security_group = shade_client.get_security_group(sg_name)
+
+    if security_group:
         log.info("Using existing security group '%s'...", sg_name)
-    else:
-        log.info("Creating security group  '%s'...", sg_name)
-        SECGROUP = create_security_group(neutron_client,
-                                         sg_name,
-                                         sg_description)
-        if not SECGROUP:
-            log.error("Failed to create the security group...")
-            return None
-
-        sg_id = SECGROUP['id']
-
-        log.debug("Security group '%s' with ID=%s created successfully.",
-                  SECGROUP['name'], sg_id)
-
-        log.debug("Adding ICMP rules in security group '%s'...", sg_name)
-        if not create_secgroup_rule(neutron_client, sg_id,
-                                    'ingress', 'icmp'):
-            log.error("Failed to create the security group rule...")
-            return None
-
-        log.debug("Adding SSH rules in security group '%s'...", sg_name)
-        if not create_secgroup_rule(
-                neutron_client, sg_id, 'ingress', 'tcp', '22', '22'):
-            log.error("Failed to create the security group rule...")
-            return None
-
-        if not create_secgroup_rule(
-                neutron_client, sg_id, 'egress', 'tcp', '22', '22'):
-            log.error("Failed to create the security group rule...")
-            return None
-    return sg_id
+        return security_group['id']
+
+    log.info("Creating security group  '%s'...", sg_name)
+    try:
+        security_group = shade_client.create_security_group(
+            sg_name, sg_description, project_id=project_id)
+    except (exc.OpenStackCloudException,
+            exc.OpenStackCloudUnavailableFeature) as op_exc:
+        log.error("Error [create_security_group(shade_client, %s, %s)]. "
+                  "Exception message: %s", sg_name, sg_description,
+                  op_exc.orig_message)
+        return
+
+    log.debug("Security group '%s' with ID=%s created successfully.",
+              security_group['name'], security_group['id'])
+
+    log.debug("Adding ICMP rules in security group '%s'...", sg_name)
+    if not create_security_group_rule(shade_client, security_group['id'],
+                                      direction='ingress', protocol='icmp'):
+        log.error("Failed to create the security group rule...")
+        shade_client.delete_security_group(sg_name)
+        return
+
+    log.debug("Adding SSH rules in security group '%s'...", sg_name)
+    if not create_security_group_rule(shade_client, security_group['id'],
+                                      direction='ingress', protocol='tcp',
+                                      port_range_min='22',
+                                      port_range_max='22'):
+        log.error("Failed to create the security group rule...")
+        shade_client.delete_security_group(sg_name)
+        return
+
+    if not create_security_group_rule(shade_client, security_group['id'],
+                                      direction='egress', protocol='tcp',
+                                      port_range_min='22',
+                                      port_range_max='22'):
+        log.error("Failed to create the security group rule...")
+        shade_client.delete_security_group(sg_name)
+        return
+    return security_group['id']
 
 
 # *********************************************
@@ -757,6 +730,18 @@ def delete_image(glance_client, image_id):    # pragma: no cover
         return True
 
 
+def list_images(shade_client=None):
+    if shade_client is None:
+        shade_client = get_shade_client()
+
+    try:
+        return shade_client.list_images()
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [list_images(shade_client)]."
+                  "Exception message, '%s'", o_exc.orig_message)
+        return False
+
+
 # *********************************************
 #   CINDER
 # *********************************************