Connect to a mongo database and read per-node pillar information.

    Parameters:
        * `collection`: The mongodb collection to read data from. Defaults to
          ``'pillar'``.
        * `id_field`: The field in the collection that represents an individual
          minion id. Defaults to ``'_id'``.
        * `re_pattern`: If your naming convention in the collection is shorter
          than the minion id, you can use this to trim the name.
          `re_pattern` will be used to match the name, and `re_replace` will
          be used to replace it. Backrefs are supported as they are in the
          Python standard library. If ``None``, no mangling of the name will
          be performed - the collection will be searched with the entire
          minion id. Defaults to ``None``.
        * `re_replace`: Use as the replacement value in node ids matched with
          `re_pattern`. Defaults to ''. Feel free to use backreferences here.
        * `fields`: The specific fields in the document to use for the pillar
          data. If ``None``, will use the entire document. If using the
          entire document, the ``_id`` field will be converted to string. Be
          careful with other fields in the document as they must be string
          serializable. Defaults to ``None``.

def ext_pillar(minion_id,
               pillar,  # pylint: disable=W0613
               collection='pillar',
               id_field='_id',
               re_pattern=None,
               re_replace='',
               fields=None):
    '''
    Connect to a mongo database and read per-node pillar information.

    Parameters:
        * `collection`: The mongodb collection to read data from. Defaults to
          ``'pillar'``.
        * `id_field`: The field in the collection that represents an individual
          minion id. Defaults to ``'_id'``.
        * `re_pattern`: If your naming convention in the collection is shorter
          than the minion id, you can use this to trim the name.
          `re_pattern` will be used to match the name, and `re_replace` will
          be used to replace it. Backrefs are supported as they are in the
          Python standard library. If ``None``, no mangling of the name will
          be performed - the collection will be searched with the entire
          minion id. Defaults to ``None``.
        * `re_replace`: Use as the replacement value in node ids matched with
          `re_pattern`. Defaults to ''. Feel free to use backreferences here.
        * `fields`: The specific fields in the document to use for the pillar
          data. If ``None``, will use the entire document. If using the
          entire document, the ``_id`` field will be converted to string. Be
          careful with other fields in the document as they must be string
          serializable. Defaults to ``None``.
    '''
    host = __opts__['mongo.host']
    port = __opts__['mongo.port']
    log.info('connecting to %s:%s for mongo ext_pillar', host, port)
    conn = pymongo.MongoClient(host, port)

    log.debug('using database \'%s\'', __opts__['mongo.db'])
    mdb = conn[__opts__['mongo.db']]

    user = __opts__.get('mongo.user')
    password = __opts__.get('mongo.password')

    if user and password:
        log.debug('authenticating as \'%s\'', user)
        mdb.authenticate(user, password)

    # Do the regex string replacement on the minion id
    if re_pattern:
        minion_id = re.sub(re_pattern, re_replace, minion_id)

    log.info(
        'ext_pillar.mongo: looking up pillar def for {\'%s\': \'%s\'} '
        'in mongo', id_field, minion_id
    )

    result = mdb[collection].find_one({id_field: minion_id}, projection=fields)
    if result:
        if fields:
            log.debug(
                'ext_pillar.mongo: found document, returning fields \'%s\'',
                fields
            )
        else:
            log.debug('ext_pillar.mongo: found document, returning whole doc')
        if '_id' in result:
            # Converting _id to a string
            # will avoid the most common serialization error cases, but DBRefs
            # and whatnot will still cause problems.
            result['_id'] = six.text_type(result['_id'])
        return result
    else:
        # If we can't find the minion the database it's not necessarily an
        # error.
        log.debug(
            'ext_pillar.mongo: no document found in collection %s',
            collection
        )
        return {}