X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=yardstick%2Fcommon%2Fopenstack_utils.py;h=541061351eca0af1639a4dc0b562dd63d8e83e14;hb=48d6322b8e64a04437b1b816cfb3d956932a5298;hp=0902d29e1838e64f28bb06a58621c92d12b29c59;hpb=aded0fdb30caf035e0eb94c1216f436ba597c2c7;p=yardstick.git diff --git a/yardstick/common/openstack_utils.py b/yardstick/common/openstack_utils.py index 0902d29e1..541061351 100644 --- a/yardstick/common/openstack_utils.py +++ b/yardstick/common/openstack_utils.py @@ -7,19 +7,20 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -import os -import sys +import copy import logging +import os +from cinderclient import client as cinderclient +from novaclient import client as novaclient +from glanceclient import client as glanceclient from keystoneauth1 import loading from keystoneauth1 import session +from neutronclient.neutron import client as neutronclient import shade from shade import exc -from cinderclient import client as cinderclient -from novaclient import client as novaclient -from glanceclient import client as glanceclient -from neutronclient.neutron import client as neutronclient +from yardstick.common import constants log = logging.getLogger(__name__) @@ -155,8 +156,30 @@ def get_glance_client(): # pragma: no cover return glanceclient.Client(get_glance_client_version(), session=sess) -def get_shade_client(): - return shade.openstack_cloud() +def get_shade_client(**os_cloud_config): + """Get Shade OpenStack cloud client + + By default, the input parameters given to "shade.openstack_cloud" method + are stored in "constants.OS_CLOUD_DEFAULT_CONFIG". The input parameters + passed in this function, "os_cloud_config", will overwrite the default + ones. + + :param os_cloud_config: (kwargs) input arguments for + "shade.openstack_cloud" method. + :return: ``shade.OpenStackCloud`` object. + """ + params = copy.deepcopy(constants.OS_CLOUD_DEFAULT_CONFIG) + params.update(os_cloud_config) + return shade.openstack_cloud(**params) + +def get_shade_operator_client(**os_cloud_config): + """Get Shade Operator cloud client + + :return: ``shade.OperatorCloud`` object. + """ + params = copy.deepcopy(constants.OS_CLOUD_DEFAULT_CONFIG) + params.update(os_cloud_config) + return shade.operator_cloud(**params) # ********************************************* @@ -264,17 +287,36 @@ def create_instance_and_wait_for_active(shade_client, name, image, "Exception message, '%s'", o_exc.orig_message) -def attach_server_volume(server_id, volume_id, - device=None): # pragma: no cover +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. + + 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. + + :returns: True if attached successful, False otherwise. + """ 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: + 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 + except exc.OpenStackCloudException as o_exc: + log.error("Error [attach_volume_to_server(shade_client)]. " + "Exception message: %s", o_exc.orig_message) + return False def delete_instance(shade_client, name_or_id, wait=False, timeout=180, @@ -302,12 +344,26 @@ def delete_instance(shade_client, name_or_id, wait=False, timeout=180, return False -def get_server_by_name(name): # pragma: no cover +def get_server(shade_client, name_or_id=None, filters=None, detailed=False, + bare=False): + """Get a server by name or ID. + + :param name_or_id: Name or ID of the server. + :param filters:(dict) A dictionary of meta data to use for further + filtering. + :param detailed:(bool) Whether or not to add detailed additional + information. + :param bare:(bool) Whether to skip adding any additional information to the + server record. + + :returns: A server ``munch.Munch`` or None if no matching server is found. + """ try: - return get_nova_client().servers.list(search_opts={'name': name})[0] - except IndexError: - log.exception('Failed to get nova client') - raise + return shade_client.get_server(name_or_id=name_or_id, filters=filters, + detailed=detailed, bare=bare) + except exc.OpenStackCloudException as o_exc: + log.error("Error [get_server(shade_client, '%s')]. " + "Exception message: %s", name_or_id, o_exc.orig_message) def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover @@ -330,12 +386,22 @@ def get_flavor_id(nova_client, flavor_name): # pragma: no cover return flavor_id -def get_flavor_by_name(name): # pragma: no cover - flavors = get_nova_client().flavors.list() +def get_flavor(shade_client, name_or_id, filters=None, get_extra=True): + """Get a flavor by name or ID. + + :param name_or_id: Name or ID of the flavor. + :param filters: A dictionary of meta data to use for further filtering. + :param get_extra: Whether or not the list_flavors call should get the extra + flavor specs. + + :returns: A flavor ``munch.Munch`` or None if no matching flavor is found. + """ try: - return next((a for a in flavors if a.name == name)) - except StopIteration: - log.exception('No flavor matched') + return shade_client.get_flavor(name_or_id, filters=filters, + get_extra=get_extra) + except exc.OpenStackCloudException as o_exc: + log.error("Error [get_flavor(shade_client, '%s')]. " + "Exception message: %s", name_or_id, o_exc.orig_message) def delete_flavor(flavor_id): # pragma: no cover @@ -667,48 +733,75 @@ def create_security_group_full(shade_client, sg_name, # ********************************************* # GLANCE # ********************************************* -def get_image_id(glance_client, image_name): # pragma: no cover - images = glance_client.images.list() - return next((i.id for i in images if i.name == image_name), None) - - -def create_image(glance_client, image_name, file_path, disk_format, - container_format, min_disk, min_ram, protected, tag, - public, **kwargs): # pragma: no cover - if not os.path.isfile(file_path): - log.error("Error: file %s does not exist.", file_path) - return None +def create_image(shade_client, name, filename=None, container='images', + md5=None, sha256=None, disk_format=None, + container_format=None, disable_vendor_agent=True, + wait=False, timeout=3600, allow_duplicates=False, meta=None, + volume=None, **kwargs): + """Upload an image. + + :param name:(str) Name of the image to create. If it is a pathname of an + image, the name will be constructed from the extensionless + basename of the path. + :param filename:(str) The path to the file to upload, if needed. + :param container:(str) Name of the container in swift where images should + be uploaded for import if the cloud requires such a thing. + :param md5:(str) md5 sum of the image file. If not given, an md5 will + be calculated. + :param sha256:(str) sha256 sum of the image file. If not given, an md5 + will be calculated. + :param disk_format:(str) The disk format the image is in. + :param container_format:(str) The container format the image is in. + :param disable_vendor_agent:(bool) Whether or not to append metadata + flags to the image to inform the cloud in + question to not expect a vendor agent to be running. + :param wait:(bool) If true, waits for image to be created. + :param timeout:(str) Seconds to wait for image creation. + :param allow_duplicates:(bool) If true, skips checks that enforce unique + image name. + :param meta:(dict) A dict of key/value pairs to use for metadata that + bypasses automatic type conversion. + :param volume:(str) Name or ID or volume object of a volume to create an + image from. + Additional kwargs will be passed to the image creation as additional + metadata for the image and will have all values converted to string + except for min_disk, min_ram, size and virtual_size which will be + converted to int. + If you are sure you have all of your data types correct or have an + advanced need to be explicit, use meta. If you are just a normal + consumer, using kwargs is likely the right choice. + If a value is in meta and kwargs, meta wins. + :returns: Image id + """ try: - image_id = get_image_id(glance_client, image_name) + image_id = shade_client.get_image_id(name) if image_id is not None: - log.info("Image %s already exists.", image_name) - else: - log.info("Creating image '%s' from '%s'...", image_name, file_path) - - image = glance_client.images.create( - name=image_name, visibility=public, disk_format=disk_format, - container_format=container_format, min_disk=min_disk, - min_ram=min_ram, tags=tag, protected=protected, **kwargs) - image_id = image.id - with open(file_path) as image_data: - glance_client.images.upload(image_id, image_data) + log.info("Image %s already exists.", name) + return image_id + log.info("Creating image '%s'", name) + image = shade_client.create_image( + name, filename=filename, container=container, md5=md5, sha256=sha256, + disk_format=disk_format, container_format=container_format, + disable_vendor_agent=disable_vendor_agent, wait=wait, timeout=timeout, + allow_duplicates=allow_duplicates, meta=meta, volume=volume, **kwargs) + image_id = image["id"] return image_id - except Exception: # pylint: disable=broad-except - log.error( - "Error [create_glance_image(glance_client, '%s', '%s', '%s')]", - image_name, file_path, public) - return None + except exc.OpenStackCloudException as op_exc: + log.error("Failed to create_image(shade_client). " + "Exception message: %s", op_exc.orig_message) -def delete_image(glance_client, image_id): # pragma: no cover +def delete_image(shade_client, name_or_id, wait=False, timeout=3600, + delete_objects=True): try: - glance_client.images.delete(image_id) + return shade_client.delete_image(name_or_id, wait=wait, + timeout=timeout, + delete_objects=delete_objects) - except Exception: # pylint: disable=broad-except - log.exception("Error [delete_flavor(glance_client, %s)]", image_id) + except exc.OpenStackCloudException as op_exc: + log.error("Failed to delete_image(shade_client). " + "Exception message: %s", op_exc.orig_message) return False - else: - return True def list_images(shade_client=None): @@ -726,54 +819,79 @@ def list_images(shade_client=None): # ********************************************* # CINDER # ********************************************* -def get_volume_id(volume_name): # pragma: no cover - volumes = get_cinder_client().volumes.list() - return next((v.id for v in volumes if v.name == volume_name), None) - - -def create_volume(cinder_client, volume_name, volume_size, - volume_image=False): # pragma: no cover - try: - if volume_image: - volume = cinder_client.volumes.create(name=volume_name, - size=volume_size, - imageRef=volume_image) - else: - volume = cinder_client.volumes.create(name=volume_name, - size=volume_size) - return volume - except Exception: # pylint: disable=broad-except - log.exception("Error [create_volume(cinder_client, %s)]", - (volume_name, volume_size)) - return None +def get_volume_id(shade_client, volume_name): + return shade_client.get_volume_id(volume_name) -def delete_volume(cinder_client, volume_id, - forced=False): # pragma: no cover - try: - if forced: - try: - cinder_client.volumes.detach(volume_id) - except Exception: # pylint: disable=broad-except - log.error(sys.exc_info()[0]) - cinder_client.volumes.force_delete(volume_id) - else: - while True: - volume = get_cinder_client().volumes.get(volume_id) - if volume.status.lower() == 'available': - break - cinder_client.volumes.delete(volume_id) - return True - except Exception: # pylint: disable=broad-except - log.exception("Error [delete_volume(cinder_client, '%s')]", volume_id) +def get_volume(shade_client, name_or_id, filters=None): + """Get a volume by name or ID. + + :param name_or_id: Name or ID of the volume. + :param filters: A dictionary of meta data to use for further filtering. + + :returns: A volume ``munch.Munch`` or None if no matching volume is found. + """ + return shade_client.get_volume(name_or_id, filters=filters) + + +def create_volume(shade_client, size, wait=True, timeout=None, + image=None, **kwargs): + """Create a volume. + + :param size: Size, in GB of the volume to create. + :param name: (optional) Name for the volume. + :param description: (optional) Name for the volume. + :param wait: If true, waits for volume to be created. + :param timeout: Seconds to wait for volume creation. None is forever. + :param image: (optional) Image name, ID or object from which to create + the volume. + + :returns: The created volume object. + + """ + try: + return shade_client.create_volume(size, wait=wait, timeout=timeout, + image=image, **kwargs) + except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as op_exc: + log.error("Failed to create_volume(shade_client). " + "Exception message: %s", op_exc.orig_message) + + +def delete_volume(shade_client, name_or_id=None, wait=True, timeout=None): + """Delete a volume. + + :param name_or_id:(string) Name or unique ID of the volume. + :param wait:(bool) If true, waits for volume to be deleted. + :param timeout:(string) Seconds to wait for volume deletion. None is forever. + + :return: True on success, False otherwise. + """ + try: + return shade_client.delete_volume(name_or_id=name_or_id, + wait=wait, timeout=timeout) + except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as o_exc: + log.error("Error [delete_volume(shade_client,'%s')]. " + "Exception message: %s", name_or_id, o_exc.orig_message) return False -def detach_volume(server_id, volume_id): # pragma: no cover +def detach_volume(shade_client, server_name_or_id, volume_name_or_id, + wait=True, timeout=None): + """Detach a volume from a server. + + :param server_name_or_id: The server name or id to detach from. + :param volume_name_or_id: The volume name or id to detach. + :param wait: If true, waits for volume to be detached. + :param timeout: Seconds to wait for volume detachment. None is forever. + + :return: True on success. + """ try: - get_nova_client().volumes.delete_server_volume(server_id, volume_id) + volume = shade_client.get_volume(volume_name_or_id) + server = get_server(shade_client, name_or_id=server_name_or_id) + shade_client.detach_volume(server, volume, wait=wait, timeout=timeout) return True - except Exception: # pylint: disable=broad-except - log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]", - server_id, volume_id) + except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as o_exc: + log.error("Error [detach_volume(shade_client)]. " + "Exception message: %s", o_exc.orig_message) return False