Add state to upload image to volume and rework result creation

Change-Id: Ic30af6a0211a4fb3dd8993c80e25c6b00d632ba0
Related-Issue: PROD-25393(PROD:25393)
diff --git a/_states/glancev2.py b/_states/glancev2.py
index 2250e25..5b149f2 100644
--- a/_states/glancev2.py
+++ b/_states/glancev2.py
@@ -20,9 +20,14 @@
     return __salt__['glancev2.{}'.format(fname)](*args, **kwargs)
 
 
-def image_present(name, cloud_name, location, image_properties,
+def _cinderv3_call(fname, *args, **kwargs):
+    return __salt__['cinderv3.{}'.format(fname)](*args, **kwargs)
+
+
+def image_present(name, cloud_name, location=None, image_properties=None,
                   import_from_format='raw', timeout=30,
-                  sleep_time=5, checksum=None):
+                  sleep_time=5, checksum=None,
+                  volume_name=None, volume_kwargs=None):
     """
     Creates a task to import an image
 
@@ -50,6 +55,10 @@
     :param timeout: (optional) Time for task to download image
     :param sleep_time: (optional) Timer countdown
     :param checksum: (optional) checksum of the image to verify it
+    :param volume_name: (optional) name of the volume if specified the command
+        works like openstack image create --volume
+    :param volume_kwargs: (optional) if volume_name is specified, this will be
+        used as arguments to image_upload_volume
     """
     try:
         exact_image = _glancev2_call(
@@ -57,86 +66,134 @@
         )
     except Exception as e:
         if 'ResourceNotFound' in repr(e):
-            image_properties['name'] = name
-            task_params = {"import_from": location,
-                           "import_from_format": import_from_format,
-                           "image_properties": image_properties
-                           }
-            # Try create task
-            try:
-                task = _glancev2_call(
-                    'task_create', task_type='import', task_input=task_params,
-                    cloud_name=cloud_name
-                )
-            except Exception as e:
-                log.error(
-                    'Glance image create failed on create task with {}'.format(
-                        e)
-                )
-                return _create_failed(name, 'image_task')
-            while timeout > 0:
-                if task['status'] == 'success':
-                    break
-                elif task['status'] == 'failure':
-                    log.error('Glance task failed to complete')
-                    return _create_failed(name, 'image')
-                else:
-                    timeout -= sleep_time
-                    time.sleep(sleep_time)
-                    # Check task status again
+            if volume_name:
+                try:
+                    _cinderv3_call(
+                        'volume_get_details', volume_name,
+                        cloud_name=cloud_name
+                    )
+                except Exception as e:
+                    if 'ResourceNotFound' in repr(e):
+                        return _failed('none', volume_name, 'volume')
+                    elif 'MultipleResourcesFound' in repr(e):
+                        return _failed('find', volume_name, 'volume')
+                    else:
+                        raise
+                if not volume_kwargs:
+                    volume_kwargs = {}
+                try:
+                    resp = _cinderv3_call(
+                        'image_upload_volume', volume_name, image_name=name,
+                        cloud_name=cloud_name, **volume_kwargs)
+                except Exception as e:
+                    log.error(
+                        'Glance create image with '
+                        'volume failed with {}'.format(e))
+                    return _failed('create', name, 'image')
+                image_status = resp['os-volume_upload_image']['status']
+                while timeout > 0 and image_status != 'active':
                     try:
-                        task = _glancev2_call(
-                            'task_show', task_id=task['id'],
-                            cloud_name=cloud_name
-                        )
+                        image = _glancev2_call('image_get_details', name=name,
+                                                   cloud_name=cloud_name)
                     except Exception as e:
-                        log.error(
-                            'Glance failed to check '
-                            'task status with {}'.format(e)
-                        )
-                        return _create_failed(name, 'image_task')
-            if timeout <= 0 and task['status'] != 'success':
-                log.error(
-                    'Glance task failed to import '
-                    'image for given amount of time'
-                )
-                return _create_failed(name, 'image')
-            # Task successfully finished
-            # and now check that is created the image
+                        if 'ResourceNotFound' in repr(e):
+                            timeout -= sleep_time
+                            time.sleep(sleep_time)
+                            continue
+                        else:
+                            raise
+                    image_status = image['status']
+                if image_status != 'active':
+                    log.error(
+                        'Glance upload image to volume failed '
+                        'for given amount of time'
+                    )
+                    return _failed('create', name, 'image')
+                return _succeeded('create', name, 'image', image)
 
-            image = _glancev2_call(
-                'image_list', name=name, cloud_name=cloud_name
-            )['images'][0]
+            else:
+                if not (location and image_properties):
+                    return _failed('create', name, 'image')
+                image_properties['name'] = name
+                task_params = {"import_from": location,
+                               "import_from_format": import_from_format,
+                               "image_properties": image_properties
+                               }
+                # Try create task
+                try:
+                    task = _glancev2_call(
+                        'task_create', task_type='import',
+                        task_input=task_params, cloud_name=cloud_name)
+                except Exception as e:
+                    log.error(
+                        'Glance image create failed on '
+                        'create task with {}'.format(
+                            e)
+                    )
+                    return _failed('create', name, 'image_task')
+                while timeout > 0:
+                    if task['status'] == 'success':
+                        break
+                    elif task['status'] == 'failure':
+                        log.error('Glance task failed to complete')
+                        return _failed('create', name, 'image')
+                    else:
+                        timeout -= sleep_time
+                        time.sleep(sleep_time)
+                        # Check task status again
+                        try:
+                            task = _glancev2_call(
+                                'task_show', task_id=task['id'],
+                                cloud_name=cloud_name
+                            )
+                        except Exception as e:
+                            log.error(
+                                'Glance failed to check '
+                                'task status with {}'.format(e)
+                            )
+                            return _failed('create', name, 'image_task')
+                if timeout <= 0 and task['status'] != 'success':
+                    log.error(
+                        'Glance task failed to import '
+                        'image for given amount of time'
+                    )
+                    return _failed('create', name, 'image')
+                # Task successfully finished
+                # and now check that is created the image
 
-            if not image:
-                return _create_failed(name, 'image')
+                images = _glancev2_call(
+                    'image_list', name=name, cloud_name=cloud_name
+                )['images']
 
-            resp = _created(name, 'image', image)
+                if not len(images):
+                    return _failed('create', name, 'image')
+                image = images[0]
+                resp = _succeeded('create', name, 'image', image)
 
-            if checksum:
-                if image['status'] == 'active':
-                    if 'checksum' not in image:
-                        log.error(
-                            'Glance image. No checksum for image.'
-                            'Image status is active'
-                        )
-                        return _create_failed(name, 'image')
-                    if image['checksum'] != checksum:
-                        log.error(
-                            'Glance image create failed since '
-                            'image_checksum should be '
-                            '{} but it is {}'.format(checksum,
-                                                     image['checksum'])
-                        )
-                        return _create_failed(name, 'image')
+                if checksum:
+                    if image['status'] == 'active':
+                        if 'checksum' not in image:
+                            log.error(
+                                'Glance image. No checksum for image.'
+                                'Image status is active'
+                            )
+                            return _failed('create', name, 'image')
+                        if image['checksum'] != checksum:
+                            log.error(
+                                'Glance image create failed since '
+                                'image_checksum should be '
+                                '{} but it is {}'.format(checksum,
+                                                         image['checksum'])
+                            )
+                            return _failed('create', name, 'image')
 
-                elif image['status'] in ['saving', 'queued']:
-                    resp['comment'] = resp['comment'] \
-                                      + " checksum couldn't be verified, " \
-                                        "since status is not active"
-            return resp
+                    elif image['status'] in ['saving', 'queued']:
+                        resp['comment'] = resp['comment'] \
+                                          + " checksum couldn't be verified, " \
+                                            "since status is not active"
+                return resp
         elif 'MultipleResourcesFound' in repr(e):
-            return _find_failed(name, 'image')
+            return _failed('find', name, 'image')
         else:
             raise
 
@@ -164,111 +221,57 @@
             )
         except Exception as e:
             log.error('Glance image update failed with {}'.format(e))
-            return _update_failed(name, 'image')
-        return _updated(name, 'image', resp)
-    return _no_changes(name, 'image')
+            return _failed('update', name, 'image')
+        return _succeeded('update', name, 'image', resp)
+    return _succeeded('no_changes', name, 'image')
 
 
 def image_absent(name, cloud_name):
     try:
-        image = _glancev2_call(
-            'image_get_details', name=name, cloud_name=cloud_name
-        )
+        _glancev2_call('image_get_details', name=name, cloud_name=cloud_name)
     except Exception as e:
         if 'ResourceNotFound' in repr(e):
-            return _absent(name, 'image')
+            return _succeeded('absent', name, 'image')
         if 'MultipleResourcesFound' in repr(e):
-            return _find_failed(name, 'image')
+            return _failed('find', name, 'image')
     try:
-        _glancev2_call(
-            'image_delete', name=name, cloud_name=cloud_name
-        )
+        _glancev2_call('image_delete', name=name, cloud_name=cloud_name)
     except Exception as e:
         log.error('Glance image delete failed with {}'.format(e))
-        return _delete_failed(name, 'image')
-    return _deleted(name, 'image')
+        return _failed('delete', name, 'image')
+    return _succeeded('delete', name, 'image')
 
 
-def _created(name, resource, resource_definition):
+def _succeeded(op, name, resource, changes=None):
+    msg_map = {
+        'create': '{0} {1} created',
+        'delete': '{0} {1} removed',
+        'update': '{0} {1} updated',
+        'no_changes': '{0} {1} is in desired state',
+        'absent': '{0} {1} not present',
+        'resources_moved': '{1} resources were moved from {0}',
+    }
     changes_dict = {
         'name': name,
-        'changes': resource_definition,
         'result': True,
-        'comment': '{}{} created'.format(resource, name)
+        'comment': msg_map[op].format(resource, name),
+        'changes': changes or {},
     }
     return changes_dict
 
-
-def _updated(name, resource, resource_definition):
-    changes_dict = {
-        'name': name,
-        'changes': resource_definition,
-        'result': True,
-        'comment': '{}{} updated'.format(resource, name)
+def _failed(op, name, resource):
+    msg_map = {
+        'create': '{0} {1} failed to create',
+        'delete': '{0} {1} failed to delete',
+        'update': '{0} {1} failed to update',
+        'find': '{0} {1} found multiple {0}',
+        'none': '{0} {1} found no {0}',
+        'resources_moved': 'failed to move {1} from {0}',
     }
-    return changes_dict
-
-
-def _no_changes(name, resource):
     changes_dict = {
         'name': name,
-        'changes': {},
-        'result': True,
-        'comment': '{}{} is in desired state'.format(resource, name)
-    }
-    return changes_dict
-
-
-def _deleted(name, resource):
-    changes_dict = {
-        'name': name,
-        'changes': {},
-        'result': True,
-        'comment': '{}{} removed'.format(resource, name)
-    }
-    return changes_dict
-
-
-def _absent(name, resource):
-    changes_dict = {'name': name,
-                    'changes': {},
-                    'comment': '{0} {1} not present'.format(resource, name),
-                    'result': True}
-    return changes_dict
-
-
-def _delete_failed(name, resource):
-    changes_dict = {'name': name,
-                    'changes': {},
-                    'comment': '{0} {1} failed to delete'.format(resource,
-                                                                 name),
-                    'result': False}
-    return changes_dict
-
-
-def _create_failed(name, resource):
-    changes_dict = {'name': name,
-                    'changes': {},
-                    'comment': '{0} {1} failed to create'.format(resource,
-                                                                 name),
-                    'result': False}
-    return changes_dict
-
-
-def _update_failed(name, resource):
-    changes_dict = {'name': name,
-                    'changes': {},
-                    'comment': '{0} {1} failed to update'.format(resource,
-                                                                 name),
-                    'result': False}
-    return changes_dict
-
-
-def _find_failed(name, resource):
-    changes_dict = {
-        'name': name,
-        'changes': {},
-        'comment': '{0} {1} found multiple {0}'.format(resource, name),
         'result': False,
+        'comment': msg_map[op].format(resource, name),
+        'changes': {},
     }
-    return changes_dict
+    return changes_dict
\ No newline at end of file