'Helper function to build jobs for a partition, this method will
read the suffix hashes and create job dictionaries to describe
the needed work.  There will be one job for each fragment index
discovered in the partition.
For a fragment index which corresponds to this node\'s ring
index, a job with job_type SYNC will be created to ensure that
the left and right hand primary ring nodes for the part have the
corresponding left and right hand fragment archives.
A fragment index (or entire partition) for which this node is
not the primary corresponding node, will create job(s) with
job_type REVERT to ensure that fragment archives are pushed to
the correct node and removed from this one.
A partition may result in multiple jobs.  Potentially many
REVERT jobs, and zero or one SYNC job.
:param local_dev: the local device (node dict)
:param part_path: full path to partition
:param partition: partition number
:param policy: the policy
:returns: a list of dicts of job info
N.B. If this function ever returns an empty list of jobs the entire
partition will be deleted.'

def _get_part_jobs(self, local_dev, part_path, partition, policy):

    try:
        hashes = self._get_hashes(local_dev['device'], partition, policy, do_listdir=True)
    except OSError as e:
        if (e.errno != errno.ENOTDIR):
            raise
        self.logger.warning(('Unexpected    entity    %r    is    not    a    directory' % part_path))
        return []
    non_data_fragment_suffixes = []
    data_fi_to_suffixes = defaultdict(list)
    for (suffix, fi_hash) in hashes.items():
        if (not fi_hash):
            continue
        data_frag_indexes = [f for f in fi_hash if (f is not None)]
        if (not data_frag_indexes):
            non_data_fragment_suffixes.append(suffix)
        else:
            for fi in data_frag_indexes:
                data_fi_to_suffixes[fi].append(suffix)
    def build_job(job_type, frag_index, suffixes, sync_to):
        return {'job_type': job_type, 'frag_index': frag_index, 'suffixes': suffixes, 'sync_to': sync_to, 'partition': partition, 'path': part_path, 'hashes': hashes, 'policy': policy, 'local_dev': local_dev, 'device': local_dev['device']}
    jobs = []
    part_nodes = policy.object_ring.get_part_nodes(partition)
    for node in part_nodes:
        if (node['id'] == local_dev['id']):
            frag_index = policy.get_backend_index(node['index'])
            try:
                suffixes = data_fi_to_suffixes.pop(frag_index)
            except KeyError:
                suffixes = []
            sync_job = build_job(job_type=SYNC, frag_index=frag_index, suffixes=suffixes, sync_to=_get_partners(node['index'], part_nodes))
            sync_job['sync_diskfile_builder'] = self.reconstruct_fa
            jobs.append(sync_job)
            break
    ordered_fis = sorted(((len(suffixes), fi) for (fi, suffixes) in data_fi_to_suffixes.items()))
    for (count, fi) in ordered_fis:
        nodes_sync_to = []
        node_index = fi
        for n in range(policy.ec_duplication_factor):
            nodes_sync_to.append(part_nodes[node_index])
            node_index += policy.ec_n_unique_fragments
        revert_job = build_job(job_type=REVERT, frag_index=fi, suffixes=data_fi_to_suffixes[fi], sync_to=nodes_sync_to)
        jobs.append(revert_job)
    if non_data_fragment_suffixes:
        if jobs:
            jobs[0]['suffixes'].extend(non_data_fragment_suffixes)
        else:
            nsample = (((policy.ec_n_unique_fragments * policy.ec_duplication_factor) - policy.ec_ndata) + 1)
            jobs.append(build_job(job_type=REVERT, frag_index=None, suffixes=non_data_fragment_suffixes, sync_to=random.sample(part_nodes, nsample)))
    return jobs
