Ensure that a grain is set

    .. versionchanged:: v2015.8.2

    name
        The grain name

    value
        The value to set on the grain

    force
        If force is True, the existing grain will be overwritten
        regardless of its existing or provided value type. Defaults to False

        .. versionadded:: v2015.8.2

    delimiter
        A delimiter different from the default can be provided.

        .. versionadded:: v2015.8.2

    It is now capable to set a grain to a complex value (ie. lists and dicts)
    and supports nested grains as well.

    If the grain does not yet exist, a new grain is set to the given value. For
    a nested grain, the necessary keys are created if they don't exist. If
    a given key is an existing value, it will be converted, but an existing value
    different from the given key will fail the state.

    If the grain with the given name exists, its value is updated to the new
    value unless its existing or provided value is complex (list or dict). Use
    `force: True` to overwrite.

    .. code-block:: yaml

      cheese:
        grains.present:
          - value: edam

      nested_grain_with_complex_value:
        grains.present:
          - name: icinga:Apache SSL
          - value:
            - command: check_https
            - params: -H localhost -p 443 -S

      with,a,custom,delimiter:
        grains.present:
          - value: yay
          - delimiter: ','

def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False):
    '''
    Ensure that a grain is set

    .. versionchanged:: v2015.8.2

    name
        The grain name

    value
        The value to set on the grain

    force
        If force is True, the existing grain will be overwritten
        regardless of its existing or provided value type. Defaults to False

        .. versionadded:: v2015.8.2

    delimiter
        A delimiter different from the default can be provided.

        .. versionadded:: v2015.8.2

    It is now capable to set a grain to a complex value (ie. lists and dicts)
    and supports nested grains as well.

    If the grain does not yet exist, a new grain is set to the given value. For
    a nested grain, the necessary keys are created if they don't exist. If
    a given key is an existing value, it will be converted, but an existing value
    different from the given key will fail the state.

    If the grain with the given name exists, its value is updated to the new
    value unless its existing or provided value is complex (list or dict). Use
    `force: True` to overwrite.

    .. code-block:: yaml

      cheese:
        grains.present:
          - value: edam

      nested_grain_with_complex_value:
        grains.present:
          - name: icinga:Apache SSL
          - value:
            - command: check_https
            - params: -H localhost -p 443 -S

      with,a,custom,delimiter:
        grains.present:
          - value: yay
          - delimiter: ','
    '''
    name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name)
    ret = {'name': name,
           'changes': {},
           'result': True,
           'comment': ''}
    _non_existent = object()
    existing = __salt__['grains.get'](name, _non_existent)
    if existing == value:
        ret['comment'] = 'Grain is already set'
        return ret
    if __opts__['test']:
        ret['result'] = None
        if existing is _non_existent:
            ret['comment'] = 'Grain {0} is set to be added'.format(name)
            ret['changes'] = {'new': name}
        else:
            ret['comment'] = 'Grain {0} is set to be changed'.format(name)
            ret['changes'] = {'changed': {name: value}}
        return ret
    ret = __salt__['grains.set'](name, value, force=force)
    if ret['result'] is True and ret['changes'] != {}:
        ret['comment'] = 'Set grain {0} to {1}'.format(name, value)
    ret['name'] = name
    return ret