'For an existing ring data set, partitions are reassigned similar to
the initial assignment.
The devices are ordered by how many partitions they still want and
kept in that order throughout the process.
The gathered partitions are iterated through, assigning them to
devices according to the "most wanted" while keeping the replicas as
"far apart" as possible.
Two different regions are considered the farthest-apart things,
followed by zones, then different ip within a zone; the
least-far-apart things are different devices with the same ip in the
same zone.
:param reassign_parts: An iterable of (part, replicas_to_replace)
pairs. replicas_to_replace is an iterable of the
replica (an int) to replace for that partition.
replicas_to_replace may be shared for multiple
partitions, so be sure you do not modify it.'

def _reassign_parts(self, reassign_parts, replica_plan):

    parts_available_in_tier = defaultdict(int)
    for dev in self._iter_devs():
        dev['sort_key'] = self._sort_key_for(dev)
        wanted = max(dev['parts_wanted'], 0)
        for tier in dev['tiers']:
            parts_available_in_tier[tier] += wanted
    available_devs = sorted((d for d in self._iter_devs() if d['weight']), key=(lambda x: x['sort_key']))
    tier2devs = defaultdict(list)
    tier2sort_key = defaultdict(tuple)
    tier2dev_sort_key = defaultdict(list)
    max_tier_depth = 0
    for dev in available_devs:
        for tier in dev['tiers']:
            tier2devs[tier].append(dev)
            tier2dev_sort_key[tier].append(dev['sort_key'])
            tier2sort_key[tier] = dev['sort_key']
            if (len(tier) > max_tier_depth):
                max_tier_depth = len(tier)
    tier2children_sets = build_tier_tree(available_devs)
    tier2children = defaultdict(list)
    tier2children_sort_key = {}
    tiers_list = [()]
    depth = 1
    while (depth <= max_tier_depth):
        new_tiers_list = []
        for tier in tiers_list:
            child_tiers = list(tier2children_sets[tier])
            child_tiers.sort(key=tier2sort_key.__getitem__)
            tier2children[tier] = child_tiers
            tier2children_sort_key[tier] = map(tier2sort_key.__getitem__, child_tiers)
            new_tiers_list.extend(child_tiers)
        tiers_list = new_tiers_list
        depth += 1
    for (part, replace_replicas) in reassign_parts:
        self._last_part_moves[part] = 0
        replicas_at_tier = defaultdict(int)
        for dev in self._devs_for_part(part):
            for tier in dev['tiers']:
                replicas_at_tier[tier] += 1
        for replica in replace_replicas:
            tier = ()
            depth = 1
            while (depth <= max_tier_depth):
                candidates = [t for t in tier2children[tier] if (replicas_at_tier[t] < replica_plan[t]['max'])]
                if (not candidates):
                    raise Exception(('no    home    for    %s/%s    %s' % (part, replica, {t: (replicas_at_tier[t], replica_plan[t]['max']) for t in tier2children[tier]})))
                tier = max(candidates, key=(lambda t: parts_available_in_tier[t]))
                depth += 1
            dev = tier2devs[tier][(-1)]
            dev['parts_wanted'] -= 1
            dev['parts'] += 1
            for tier in dev['tiers']:
                parts_available_in_tier[tier] -= 1
                replicas_at_tier[tier] += 1
            self._replica2part2dev[replica][part] = dev['id']
            self.logger.debug('Placed    %d/%d    onto    dev    %s', part, replica, pretty_dev(dev))
    for dev in self._iter_devs():
        del dev['sort_key']
