Closing keystone sessions after done with them.
[snaps.git] / snaps / openstack / utils / glance_utils.py
index f4b4466..db20f2f 100644 (file)
@@ -34,32 +34,62 @@ Utilities for basic neutron API calls
 """
 
 
-def glance_client(os_creds):
+def glance_client(os_creds, session=None):
     """
     Creates and returns a glance client object
+    :param os_creds: the credentials for connecting to the OpenStack remote API
+    :param session: the keystone session object (optional)
     :return: the glance client
     """
-    return Client(version=os_creds.image_api_version, session=keystone_utils.keystone_session(os_creds))
+    if not session:
+        session = keystone_utils.keystone_session(os_creds)
 
+    return Client(version=os_creds.image_api_version,
+                  session=session,
+                  region_name=os_creds.region_name)
 
-def get_image(glance, image_name=None):
+
+def get_image(glance, image_name=None, image_settings=None):
     """
     Returns an OpenStack image object for a given name
     :param glance: the Glance client
     :param image_name: the image name to lookup
+    :param image_settings: the image settings used for lookups
     :return: the image object or None
     """
-    images = glance.images.list()
+    img_filter = dict()
+    if image_settings:
+        if image_settings.exists:
+            img_filter = {'name': image_settings.name}
+        else:
+            img_filter = {'name': image_settings.name,
+                          'disk_format': image_settings.format}
+    elif image_name:
+        img_filter = {'name': image_name}
+
+    images = glance.images.list(**{'filters': img_filter})
     for image in images:
         if glance.version == VERSION_1:
-            if image.name == image_name:
-                image = glance.images.get(image.id)
-                return Image(name=image.name, image_id=image.id, size=image.size, properties=image.properties)
+            image = glance.images.get(image.id)
+            return Image(name=image.name, image_id=image.id,
+                         size=image.size, properties=image.properties)
         elif glance.version == VERSION_2:
-            if image['name'] == image_name:
-                return Image(name=image['name'], image_id=image['id'], size=image['size'],
-                             properties=image.get('properties'))
-    return None
+            return Image(
+                name=image['name'], image_id=image['id'],
+                size=image['size'], properties=image.get('properties'))
+
+
+def get_image_by_id(glance, image_id):
+    """
+    Returns an OpenStack image object for a given name
+    :param glance: the Glance client
+    :param image_id: the image ID to lookup
+    :return: the SNAPS-OO Domain Image object or None
+    """
+    image = glance.images.get(image_id)
+    return Image(
+        name=image['name'], image_id=image['id'],
+        size=image['size'], properties=image.get('properties'))
 
 
 def get_image_status(glance, image):
@@ -76,7 +106,7 @@ def get_image_status(glance, image):
         os_image = glance.images.get(image.id)
         return os_image['status']
     else:
-        raise Exception('Unsupported glance client version')
+        raise GlanceException('Unsupported glance client version')
 
 
 def create_image(glance, image_settings):
@@ -92,7 +122,7 @@ def create_image(glance, image_settings):
     elif glance.version == VERSION_2:
         return __create_image_v2(glance, image_settings)
     else:
-        raise Exception('Unsupported glance client version')
+        raise GlanceException('Unsupported glance client version')
 
 
 def __create_image_v1(glance, image_settings):
@@ -101,34 +131,36 @@ def __create_image_v1(glance, image_settings):
     :param glance: the glance client
     :param image_settings: the image settings object
     :return: the OpenStack image object
-    :raise Exception if using a file and it cannot be found
+    :raise exceptions from the Glance client or IOError when opening a file
     """
-    created_image = None
+    kwargs = {
+        'name': image_settings.name, 'disk_format': image_settings.format,
+        'container_format': 'bare', 'is_public': image_settings.public}
 
-    # TODO/REFACTOR - replace each call with one including kwargs
-    if image_settings.url:
-        if image_settings.extra_properties:
-            created_image = glance.images.create(
-                name=image_settings.name, disk_format=image_settings.format, container_format="bare",
-                location=image_settings.url, properties=image_settings.extra_properties,
-                is_public=image_settings.public)
-        else:
-            created_image = glance.images.create(name=image_settings.name, disk_format=image_settings.format,
-                                                 container_format="bare", location=image_settings.url,
-                                                 is_public=image_settings.public)
-    elif image_settings.image_file:
-        image_file = open(image_settings.image_file, 'rb')
+    image_file = None
+
+    try:
         if image_settings.extra_properties:
-            created_image = glance.images.create(
-                name=image_settings.name, disk_format=image_settings.format, container_format="bare", data=image_file,
-                properties=image_settings.extra_properties, is_public=image_settings.public)
+            kwargs['properties'] = image_settings.extra_properties
+
+        if image_settings.url:
+            kwargs['location'] = image_settings.url
+        elif image_settings.image_file:
+            image_file = open(image_settings.image_file, 'rb')
+            kwargs['data'] = image_file
         else:
-            created_image = glance.images.create(
-                name=image_settings.name, disk_format=image_settings.format, container_format="bare", data=image_file,
-                is_public=image_settings.public)
+            logger.warn(
+                'Unable to create image with name - %s. No file or URL',
+                image_settings.name)
+            return None
 
-    return Image(name=image_settings.name, image_id=created_image.id, size=created_image.size,
-                 properties=created_image.properties)
+        created_image = glance.images.create(**kwargs)
+        return Image(name=image_settings.name, image_id=created_image.id,
+                     size=created_image.size,
+                     properties=created_image.properties)
+    finally:
+        if image_file:
+            image_file.close()
 
 
 def __create_image_v2(glance, image_settings):
@@ -137,26 +169,28 @@ def __create_image_v2(glance, image_settings):
     :param glance: the glance client v2
     :param image_settings: the image settings object
     :return: the OpenStack image object
-    :raise Exception if using a file and it cannot be found
+    :raise GlanceException or IOException or URLError
     """
     cleanup_temp_file = False
     image_file = None
-    if image_settings.image_file:
+    if image_settings.image_file is not None:
         image_filename = image_settings.image_file
     elif image_settings.url:
         file_name = str(uuid.uuid4())
         try:
-            image_file = file_utils.download(image_settings.url, './tmp', file_name)
+            image_file = file_utils.download(
+                image_settings.url, './tmp', file_name)
             image_filename = image_file.name
         except:
-            os.remove('./tmp/' + file_name)
+            if image_file:
+                os.remove(image_file.name)
             raise
 
         cleanup_temp_file = True
     else:
-        raise Exception('Filename or URL of image not configured')
+        raise GlanceException('Filename or URL of image not configured')
 
-    created_image = None
+    os_image = None
     try:
         kwargs = dict()
         kwargs['name'] = image_settings.name
@@ -169,23 +203,25 @@ def __create_image_v2(glance, image_settings):
         if image_settings.extra_properties:
             kwargs.update(image_settings.extra_properties)
 
-        created_image = glance.images.create(**kwargs)
-        image_file = open(image_filename, 'rb')
-        glance.images.upload(created_image['id'], image_file)
+        os_image = glance.images.create(**kwargs)
+        image_file = open(os.path.expanduser(image_filename), 'rb')
+        glance.images.upload(os_image['id'], image_file)
     except:
         logger.error('Unexpected exception creating image. Rolling back')
-        if created_image:
-            delete_image(glance, created_image)
+        if os_image:
+            delete_image(glance, Image(
+                name=os_image['name'], image_id=os_image['id'],
+                size=os_image['size'], properties=os_image.get('properties')))
         raise
     finally:
         if image_file:
+            logger.debug('Closing file %s', image_file.name)
             image_file.close()
         if cleanup_temp_file:
+            logger.info('Removing file %s', image_file.name)
             os.remove(image_filename)
 
-    updated_image = glance.images.get(created_image['id'])
-    return Image(name=updated_image['name'], image_id=updated_image['id'], size=updated_image['size'],
-                 properties=updated_image.get('properties'))
+    return get_image_by_id(glance, os_image['id'])
 
 
 def delete_image(glance, image):
@@ -194,9 +230,11 @@ def delete_image(glance, image):
     :param glance: the glance client
     :param image: the image to delete
     """
-    if glance.version == VERSION_1:
-        glance.images.delete(image)
-    elif glance.version == VERSION_2:
-        glance.images.delete(image.id)
-    else:
-        raise Exception('Unsupported glance client version')
+    logger.info('Deleting image named - %s', image.name)
+    glance.images.delete(image.id)
+
+
+class GlanceException(Exception):
+    """
+    Exception when calls to the Glance client cannot be served properly
+    """