Ensure a user is present with the specified groups

    name
        Name of user

    password
        Encrypted or Plain Text password for user

    roles
        List of roles the user should be assigned.  Any roles not in this list will be removed

    encrypted
        Whether the password is encrypted already or not.  Defaults to False

    crypt_salt
        Salt to use when encrypting the password.  Default is None (salt is
        randomly generated for unhashed passwords)

    algorithm
        Algorithm to use for hashing password.  Defaults to sha256.
        Accepts md5, blowfish, sha256, sha512

        .. note: sha512 may make the hash too long to save in NX OS which limits the has to 64 characters

    Examples:

    .. code-block:: yaml

        create:
          onyx.user_present:
            - name: daniel
            - roles:
              - vdc-admin

        set_password:
          onyx.user_present:
            - name: daniel
            - password: admin
            - roles:
              - network-admin

        update:
          onyx.user_present:
            - name: daniel
            - password: AiN9jaoP
            - roles:
              - network-admin
              - vdc-admin

def user_present(name, password=None, roles=None, encrypted=False, crypt_salt=None, algorithm='sha256'):
    '''
    Ensure a user is present with the specified groups

    name
        Name of user

    password
        Encrypted or Plain Text password for user

    roles
        List of roles the user should be assigned.  Any roles not in this list will be removed

    encrypted
        Whether the password is encrypted already or not.  Defaults to False

    crypt_salt
        Salt to use when encrypting the password.  Default is None (salt is
        randomly generated for unhashed passwords)

    algorithm
        Algorithm to use for hashing password.  Defaults to sha256.
        Accepts md5, blowfish, sha256, sha512

        .. note: sha512 may make the hash too long to save in NX OS which limits the has to 64 characters

    Examples:

    .. code-block:: yaml

        create:
          onyx.user_present:
            - name: daniel
            - roles:
              - vdc-admin

        set_password:
          onyx.user_present:
            - name: daniel
            - password: admin
            - roles:
              - network-admin

        update:
          onyx.user_present:
            - name: daniel
            - password: AiN9jaoP
            - roles:
              - network-admin
              - vdc-admin

    '''
    ret = {'name': name,
           'result': False,
           'changes': {},
           'comment': ''}

    change_password = False
    if password is not None:
        change_password = not __salt__['onyx.cmd']('check_password', username=name,
                                                   password=password, encrypted=encrypted)

    change_roles = False
    if roles is not None:
        cur_roles = __salt__['onyx.cmd']('get_roles', username=name)
        change_roles = set(roles) != set(cur_roles)

    old_user = __salt__['onyx.cmd']('get_user', username=name)

    if not any([change_password, change_roles, not old_user]):
        ret['result'] = True
        ret['comment'] = 'User already exists'
        return ret

    if change_roles is True:
        remove_roles = set(cur_roles) - set(roles)
        add_roles = set(roles) - set(cur_roles)

    if __opts__['test'] is True:
        ret['result'] = None
        if not old_user:
            ret['comment'] = 'User will be created'
            if password is not None:
                ret['changes']['password'] = True
            if roles is not None:
                ret['changes']['role'] = {'add': roles,
                                          'remove': [], }
            return ret
        if change_password is True:
            ret['comment'] = 'User will be updated'
            ret['changes']['password'] = True
        if change_roles is True:
            ret['comment'] = 'User will be updated'
            ret['changes']['roles'] = {'add': list(add_roles), 'remove': list(remove_roles)}
        return ret

    if change_password is True:
        new_user = __salt__['onyx.cmd']('set_password', username=name, password=password,
                                        encrypted=encrypted, role=roles[0] if roles else None,
                                        crypt_salt=crypt_salt, algorithm=algorithm)
        ret['changes']['password'] = {
            'new': new_user,
            'old': old_user,
        }
    if change_roles is True:
        for role in add_roles:
            __salt__['onyx.cmd']('set_role', username=name, role=role)
        for role in remove_roles:
            __salt__['onyx.cmd']('unset_role', username=name, role=role)
        ret['changes']['roles'] = {
            'new': __salt__['onyx.cmd']('get_roles', username=name),
            'old': cur_roles,
        }

    correct_password = True
    if password is not None:
        correct_password = __salt__['onyx.cmd']('check_password', username=name, password=password,
                                                encrypted=encrypted)

    correct_roles = True
    if roles is not None:
        cur_roles = __salt__['onyx.cmd']('get_roles', username=name)
        correct_roles = set(roles) != set(cur_roles)

    if not correct_roles:
        ret['comment'] = 'Failed to set correct roles'
    elif not correct_password:
        ret['comment'] = 'Failed to set correct password'
    else:
        ret['comment'] = 'User set correctly'
        ret['result'] = True

    return ret