'Reconstructs a fragment archive - this method is called from ssync
after a remote node responds that is missing this object - the local
diskfile is opened to provide metadata - but to reconstruct the
missing fragment archive we must connect to multiple object servers.
:param job: job from ssync_sender
:param node: node that we\'re rebuilding to
:param datafile_metadata:  the datafile metadata to attach to
the rebuilt fragment archive
:returns: a DiskFile like class for use by ssync
:raises DiskFileError: if the fragment archive cannot be reconstructed'

def reconstruct_fa(self, job, node, datafile_metadata):

    part_nodes = job['policy'].object_ring.get_part_nodes(job['partition'])
    part_nodes.remove(node)
    fi_to_rebuild = job['policy'].get_backend_index(node['index'])
    headers = self.headers.copy()
    headers['X-Backend-Storage-Policy-Index'] = int(job['policy'])
    frag_prefs = [{'timestamp': datafile_metadata['X-Timestamp'], 'exclude': []}]
    headers['X-Backend-Fragment-Preferences'] = json.dumps(frag_prefs)
    pile = GreenAsyncPile(len(part_nodes))
    path = datafile_metadata['name']
    for _node in part_nodes:
        full_get_path = _full_path(_node, job['partition'], path, job['policy'])
        pile.spawn(self._get_response, _node, job['partition'], path, headers, full_get_path)
    buckets = defaultdict(dict)
    etag_buckets = {}
    error_resp_count = 0
    for resp in pile:
        if (not resp):
            error_resp_count += 1
            continue
        resp.headers = HeaderKeyDict(resp.getheaders())
        frag_index = resp.headers.get('X-Object-Sysmeta-Ec-Frag-Index')
        try:
            resp_frag_index = int(frag_index)
        except (TypeError, ValueError):
            self.logger.warning('Invalid    resp    from    %s    (invalid    X-Object-Sysmeta-Ec-Frag-Index:    %r)', resp.full_path, frag_index)
            continue
        if (fi_to_rebuild == resp_frag_index):
            self.logger.debug('Found    existing    frag    #%s    at    %s    while    rebuilding    to    %s', fi_to_rebuild, resp.full_path, _full_path(node, job['partition'], datafile_metadata['name'], job['policy']))
            continue
        timestamp = resp.headers.get('X-Backend-Timestamp')
        if (not timestamp):
            self.logger.warning('Invalid    resp    from    %s,    frag    index    %s    (missing    X-Backend-Timestamp)', resp.full_path, resp_frag_index)
            continue
        timestamp = Timestamp(timestamp)
        etag = resp.headers.get('X-Object-Sysmeta-Ec-Etag')
        if (not etag):
            self.logger.warning('Invalid    resp    from    %s,    frag    index    %s    (missing    Etag)', resp.full_path, resp_frag_index)
            continue
        if (etag != etag_buckets.setdefault(timestamp, etag)):
            self.logger.error('Mixed    Etag    (%s,    %s)    for    %s    frag#%s', etag, etag_buckets[timestamp], _full_path(node, job['partition'], datafile_metadata['name'], job['policy']), fi_to_rebuild)
            continue
        if (resp_frag_index not in buckets[timestamp]):
            buckets[timestamp][resp_frag_index] = resp
            if (len(buckets[timestamp]) >= job['policy'].ec_ndata):
                responses = buckets[timestamp].values()
                self.logger.debug(('Reconstruct    frag    #%s    with    frag    indexes    %s' % (fi_to_rebuild, list(buckets[timestamp]))))
                break
    else:
        for (timestamp, resp) in sorted(buckets.items()):
            etag = etag_buckets[timestamp]
            self.logger.error(('Unable    to    get    enough    responses    (%s/%s)    to    reconstruct    %s    frag#%s    with    ETag    %s' % (len(resp), job['policy'].ec_ndata, _full_path(node, job['partition'], datafile_metadata['name'], job['policy']), fi_to_rebuild, etag)))
        if error_resp_count:
            self.logger.error(('Unable    to    get    enough    responses    (%s    error    responses)    to    reconstruct    %s    frag#%s' % (error_resp_count, _full_path(node, job['partition'], datafile_metadata['name'], job['policy']), fi_to_rebuild)))
        raise DiskFileError('Unable    to    reconstruct    EC    archive')
    rebuilt_fragment_iter = self.make_rebuilt_fragment_iter(responses[:job['policy'].ec_ndata], path, job['policy'], fi_to_rebuild)
    return RebuildingECDiskFileStream(datafile_metadata, fi_to_rebuild, rebuilt_fragment_iter)
