import collections


Host = collections.namedtuple('Host', 'stateMachine allocation')


class OutOfResourcesError(Exception):
    pass


class Priority:
    _NICE = {'user': (0, 1), 'racktest': (0.2, 1.2), 'dirbalak': (0.1, 1.1), 'default': (1, 2)}

    def __init__(self, requirements, allocationInfo, freePool, allocations):
        assert len(requirements) > 0
        self._requirements = requirements
        self._allocationInfo = allocationInfo
        self._freePool = freePool
        self._allocations = allocations
        self._allocated = self._allocate()

    def allocated(self):
        return self._allocated

    def _absoluteNice(self, allocationInfo):
        range = self._NICE.get(allocationInfo['purpose'], self._NICE['default'])
        return (range[1] - range[0]) * allocationInfo['nice'] + range[0]

    def _allocationsSortedByAscendingAge(self):
        livingAllocations = [allocation for allocation in self._allocations if allocation.dead() is None]
        livingAllocationsFromOldestToYoungest = list(livingAllocations)
        livingAllocationsFromYoungestToOldest = reversed(livingAllocationsFromOldestToYoungest)
        return livingAllocationsFromYoungestToOldest

    def _allocationsStableSortedByDescendingNice(self, allocations):
        allocations = list(allocations)
        allocations.sort(key=lambda x: -self._absoluteNice(x.allocationInfo()))
        return allocations

    def _freeAndNicer(self):
        result = [Host(s, None) for s in self._freePool.all()]
        myNice = self._absoluteNice(self._allocationInfo)
        allocationsFromYoungestToOldest = self._allocationsSortedByAscendingAge()
        nicerAllocationsFromYoungestToOldest = \
            [allocation for allocation in allocationsFromYoungestToOldest if
             self._absoluteNice(allocation.allocationInfo()) > myNice]
        nicerAllocationsSortedByDescendingNiceAndByAscendingAge = \
            self._allocationsStableSortedByDescendingNice(nicerAllocationsFromYoungestToOldest)
        for allocation in nicerAllocationsSortedByDescendingNiceAndByAscendingAge:
            result += [Host(s, allocation) for s in allocation.allocated().values()]
        return result

    def _allocate(self):
        freeAndNicer = self._freeAndNicer()
        allocated = []
        for name, requirement in self._requirements.iteritems():
            fulfilled = False
            for host in freeAndNicer:
                if host.stateMachine.hostImplementation().fulfillsRequirement(requirement):
                    allocated.append((name, host))
                    freeAndNicer.remove(host)
                    fulfilled = True
                    break
            if not fulfilled:
                raise OutOfResourcesError(
                    "Not enough machines free or busy doing lower priority tasks to allocate "
                    "requested machines")
        self._withdrawExistingAllocations(allocated)
        self._takeOutOfFreePool(allocated)
        return {name: h.stateMachine for name, h in allocated}

    def _withdrawExistingAllocations(self, allocated):
        toWithdraw = set([h[1].allocation for h in allocated if h[1].allocation is not None])
        for allocation in toWithdraw:
            allocation.withdraw("An allocation with higher priority needs resources")

    def _takeOutOfFreePool(self, allocated):
        for name, host in allocated:
            self._freePool.takeOut(host.stateMachine)

@@ -3091,32 +3091,641 @@
 lf, allocated):%0A
+        prioritizedUser = self._allocationInfo.get(%22user%22, %22Unknown%22)%0A        prioritizedPurpose = self._allocationInfo%5B%22purpose%22%5D%0A        prioritizedNice = self._allocationInfo%5B%22nice%22%5D%0A        prioritizedAbsNice = self._absoluteNice(self._allocationInfo)%0A        msg = %22An allocation with a higher priority needs your resources; User '%25(user)s' with purpose %22 %5C%0A              %22'%25(purpose)s', nice: '%25(nice)s' (ABSOLUTE NICE: '%25(absNice)s') trumps your allocation: %22 %25 %5C%0A              dict(user=prioritizedUser, purpose=prioritizedPurpose, nice=prioritizedNice,%0A                   absNice=prioritizedAbsNice)%0A
         toWithdr
@@ -3843,32 +3843,39 @@
 raw:%0A           
+ info =
  allocation.with
@@ -3874,69 +3874,597 @@
 ion.
-withdraw(%22An allocation with higher priority needs resources%22
+allocationInfo()%0A            myAbsNice = self._absoluteNice(info)%0A            allocationMsg = %22User: %25(user)s, purpose: %25(purpose)s, nice: %25(nice)s, %22 %5C%0A                            %22(ABSOLUTE NICE:%25(absNice)s)%22 %25 dict(user=info.get(%22user%22, %22Unknown%22),%0A                                                                 purpose=info%5B%22purpose%22%5D,%0A                                                                 nice=info%5B%22nice%22%5D,%0A                                                                 absNice=myAbsNice)%0A            curMsg = msg + allocationMsg%0A            allocation.withdraw(curMsg
 )%0A%0A 

Send details about the allocation that preempts your allocation