'Collect object responses to a PUT request and determine if a
satisfactory number of nodes have returned success.  Returns
lists of accumulated status codes, reasons, bodies and etags.
:param req: the request
:param putters: list of putters for the request
:param num_nodes: number of nodes involved
:param final_phase: boolean indicating if this is the last phase
:param min_responses: minimum needed when not requiring quorum
:return: a tuple of lists of status codes, reasons, bodies and etags.
The list of bodies and etags is only populated for the final
phase of a PUT transaction.'

def _get_put_responses(self, req, putters, num_nodes, final_phase=True, min_responses=None):

    statuses = []
    reasons = []
    bodies = []
    etags = set()
    pile = GreenAsyncPile(len(putters))
    for putter in putters:
        if putter.failed:
            continue
        pile.spawn(self._get_conn_response, putter, req.path, self.app.logger.thread_locals, final_phase=final_phase)
    def _handle_response(putter, response):
        statuses.append(response.status)
        reasons.append(response.reason)
        if final_phase:
            body = response.read()
        else:
            body = ''
        bodies.append(body)
        if (response.status == HTTP_INSUFFICIENT_STORAGE):
            putter.failed = True
            self.app.error_limit(putter.node, _('ERROR    Insufficient    Storage'))
        elif (response.status >= HTTP_INTERNAL_SERVER_ERROR):
            putter.failed = True
            self.app.error_occurred(putter.node, (_('ERROR    %(status)d    %(body)s    From    Object    Server    re:    %(path)s') % {'status': response.status, 'body': body[:1024], 'path': req.path}))
        elif is_success(response.status):
            etags.add(response.getheader('etag').strip('"'))
    for (putter, response) in pile:
        if response:
            _handle_response(putter, response)
            if self._have_adequate_put_responses(statuses, num_nodes, min_responses):
                break
        else:
            putter.failed = True
    finished_quickly = pile.waitall(self.app.post_quorum_timeout)
    for (putter, response) in finished_quickly:
        if response:
            _handle_response(putter, response)
    if final_phase:
        while (len(statuses) < num_nodes):
            statuses.append(HTTP_SERVICE_UNAVAILABLE)
            reasons.append('')
            bodies.append('')
    return (statuses, reasons, bodies, etags)
