Ensure that the keystone user is present with the specified properties.

    name
        The name of the user to manage

    password
        The password to use for this user.

        .. note::

            If the user already exists and a different password was set for
            the user than the one specified here, the password for the user
            will be updated. Please set the ``password_reset`` option to
            ``False`` if this is not the desired behavior.

    password_reset
        Whether or not to reset password after initial set. Defaults to
        ``True``.

    email
        The email address for this user

    tenant
        The tenant (name) for this user

    project
        The project (name) for this user (overrides tenant in api v3)

    enabled
        Availability state for this user

    roles
        The roles the user should have under given tenants.
        Passed as a dictionary mapping tenant names to a list
        of roles in this tenant, i.e.::

            roles:
                admin:   # tenant
                  - admin  # role
                service:
                  - admin
                  - Member

def user_present(name,
                 password,
                 email,
                 tenant=None,
                 enabled=True,
                 roles=None,
                 profile=None,
                 password_reset=True,
                 project=None,
                 **connection_args):
    '''
    Ensure that the keystone user is present with the specified properties.

    name
        The name of the user to manage

    password
        The password to use for this user.

        .. note::

            If the user already exists and a different password was set for
            the user than the one specified here, the password for the user
            will be updated. Please set the ``password_reset`` option to
            ``False`` if this is not the desired behavior.

    password_reset
        Whether or not to reset password after initial set. Defaults to
        ``True``.

    email
        The email address for this user

    tenant
        The tenant (name) for this user

    project
        The project (name) for this user (overrides tenant in api v3)

    enabled
        Availability state for this user

    roles
        The roles the user should have under given tenants.
        Passed as a dictionary mapping tenant names to a list
        of roles in this tenant, i.e.::

            roles:
                admin:   # tenant
                  - admin  # role
                service:
                  - admin
                  - Member
    '''
    ret = {'name': name,
           'changes': {},
           'result': True,
           'comment': 'User "{0}" will be updated'.format(name)}

    _api_version(profile=profile, **connection_args)

    if project and not tenant:
        tenant = project

    # Validate tenant if set
    if tenant is not None:
        tenantdata = __salt__['keystone.tenant_get'](name=tenant,
                                                     profile=profile,
                                                     **connection_args)
        if 'Error' in tenantdata:
            ret['result'] = False
            ret['comment'] = 'Tenant / project "{0}" does not exist'.format(tenant)
            return ret
        tenant_id = tenantdata[tenant]['id']
    else:
        tenant_id = None

    # Check if user is already present
    user = __salt__['keystone.user_get'](name=name, profile=profile,
                                         **connection_args)
    if 'Error' not in user:

        change_email = False
        change_enabled = False
        change_tenant = False
        change_password = False

        if user[name].get('email', None) != email:
            change_email = True

        if user[name].get('enabled', None) != enabled:
            change_enabled = True

        if tenant and (_TENANT_ID not in user[name] or
                       user[name].get(_TENANT_ID, None) != tenant_id):
            change_tenant = True

        if (password_reset is True and
            not __salt__['keystone.user_verify_password'](name=name,
                                                          password=password,
                                                          profile=profile,
                                                          **connection_args)):
            change_password = True

        if __opts__.get('test') and (change_email or change_enabled or change_tenant or change_password):
            ret['result'] = None
            ret['comment'] = 'User "{0}" will be updated'.format(name)
            if change_email is True:
                ret['changes']['Email'] = 'Will be updated'
            if change_enabled is True:
                ret['changes']['Enabled'] = 'Will be True'
            if change_tenant is True:
                ret['changes']['Tenant'] = 'Will be added to "{0}" tenant'.format(tenant)
            if change_password is True:
                ret['changes']['Password'] = 'Will be updated'
            return ret

        ret['comment'] = 'User "{0}" is already present'.format(name)

        if change_email:
            __salt__['keystone.user_update'](name=name, email=email, profile=profile, **connection_args)
            ret['comment'] = 'User "{0}" has been updated'.format(name)
            ret['changes']['Email'] = 'Updated'

        if change_enabled:
            __salt__['keystone.user_update'](name=name, enabled=enabled, profile=profile, **connection_args)
            ret['comment'] = 'User "{0}" has been updated'.format(name)
            ret['changes']['Enabled'] = 'Now {0}'.format(enabled)

        if change_tenant:
            __salt__['keystone.user_update'](name=name, tenant=tenant, profile=profile, **connection_args)
            ret['comment'] = 'User "{0}" has been updated'.format(name)
            ret['changes']['Tenant'] = 'Added to "{0}" tenant'.format(tenant)

        if change_password:
            __salt__['keystone.user_password_update'](name=name, password=password, profile=profile,
                                                      **connection_args)
            ret['comment'] = 'User "{0}" has been updated'.format(name)
            ret['changes']['Password'] = 'Updated'

        if roles:
            for tenant in roles:
                args = dict({'user_name': name, 'tenant_name':
                             tenant, 'profile': profile}, **connection_args)
                tenant_roles = __salt__['keystone.user_role_list'](**args)
                for role in roles[tenant]:
                    if role not in tenant_roles:
                        if __opts__.get('test'):
                            ret['result'] = None
                            ret['comment'] = 'User roles "{0}" will been updated'.format(name)
                            return ret
                        addargs = dict({'user': name, 'role': role,
                                        'tenant': tenant,
                                        'profile': profile},
                                       **connection_args)
                        newrole = __salt__['keystone.user_role_add'](**addargs)
                        if 'roles' in ret['changes']:
                            ret['changes']['roles'].append(newrole)
                        else:
                            ret['changes']['roles'] = [newrole]
                roles_to_remove = list(set(tenant_roles) - set(roles[tenant]))
                for role in roles_to_remove:
                    if __opts__.get('test'):
                        ret['result'] = None
                        ret['comment'] = 'User roles "{0}" will been updated'.format(name)
                        return ret
                    addargs = dict({'user': name, 'role': role,
                                    'tenant': tenant,
                                    'profile': profile},
                                   **connection_args)
                    oldrole = __salt__['keystone.user_role_remove'](**addargs)
                    if 'roles' in ret['changes']:
                        ret['changes']['roles'].append(oldrole)
                    else:
                        ret['changes']['roles'] = [oldrole]
    else:
        # Create that user!
        if __opts__.get('test'):
            ret['result'] = None
            ret['comment'] = 'Keystone user "{0}" will be added'.format(name)
            ret['changes']['User'] = 'Will be created'
            return ret
        __salt__['keystone.user_create'](name=name,
                                         password=password,
                                         email=email,
                                         tenant_id=tenant_id,
                                         enabled=enabled,
                                         profile=profile,
                                         **connection_args)
        if roles:
            for tenant in roles:
                for role in roles[tenant]:
                    __salt__['keystone.user_role_add'](user=name,
                                                       role=role,
                                                       tenant=tenant,
                                                       profile=profile,
                                                       **connection_args)
        ret['comment'] = 'Keystone user {0} has been added'.format(name)
        ret['changes']['User'] = 'Created'

    return ret