Runs a function from an execution module creating pre and post snapshots
    and associating the salt job id with those snapshots for easy undo and
    cleanup.

    function
        Salt function to call.

    config
        Configuration name. (default: "root")

    description
        A description for the snapshots. (default: None)

    userdata
        Data to include in the snapshot metadata. (default: None)

    cleanup_algorithm
        Snapper cleanup algorithm. (default: "number")

    `*args`
        args for the function to call. (default: None)

    `**kwargs`
        kwargs for the function to call (default: None)

    This  would run append text to /etc/motd using the file.append
    module, and will create two snapshots, pre and post with the associated
    metadata. The jid will be available as salt_jid in the userdata of the
    snapshot.

    You can immediately see the changes

    CLI Example:

    .. code-block:: bash

        salt '*' snapper.run file.append args='["/etc/motd", "some text"]'

def run(function, *args, **kwargs):
    '''
    Runs a function from an execution module creating pre and post snapshots
    and associating the salt job id with those snapshots for easy undo and
    cleanup.

    function
        Salt function to call.

    config
        Configuration name. (default: "root")

    description
        A description for the snapshots. (default: None)

    userdata
        Data to include in the snapshot metadata. (default: None)

    cleanup_algorithm
        Snapper cleanup algorithm. (default: "number")

    `*args`
        args for the function to call. (default: None)

    `**kwargs`
        kwargs for the function to call (default: None)

    This  would run append text to /etc/motd using the file.append
    module, and will create two snapshots, pre and post with the associated
    metadata. The jid will be available as salt_jid in the userdata of the
    snapshot.

    You can immediately see the changes

    CLI Example:

    .. code-block:: bash

        salt '*' snapper.run file.append args='["/etc/motd", "some text"]'
    '''
    config = kwargs.pop("config", "root")
    description = kwargs.pop("description", "snapper.run[{0}]".format(function))
    cleanup_algorithm = kwargs.pop("cleanup_algorithm", "number")
    userdata = kwargs.pop("userdata", {})

    func_kwargs = dict((k, v) for k, v in kwargs.items() if not k.startswith('__'))
    kwargs = dict((k, v) for k, v in kwargs.items() if k.startswith('__'))

    pre_nr = __salt__['snapper.create_snapshot'](
        config=config,
        snapshot_type='pre',
        description=description,
        cleanup_algorithm=cleanup_algorithm,
        userdata=userdata,
        **kwargs)

    if function not in __salt__:
        raise CommandExecutionError(
            'function "{0}" does not exist'.format(function)
        )

    try:
        ret = __salt__[function](*args, **func_kwargs)
    except CommandExecutionError as exc:
        ret = "\n".join([six.text_type(exc), __salt__[function].__doc__])

    __salt__['snapper.create_snapshot'](
        config=config,
        snapshot_type='post',
        pre_number=pre_nr,
        description=description,
        cleanup_algorithm=cleanup_algorithm,
        userdata=userdata,
        **kwargs)
    return ret