private Retirement shouldRetire(NodeCandidate candidate, List<NodeCandidate> candidates) {
        if ( ! requested.considerRetiring()) {
            boolean alreadyRetired = candidate.allocation().map(a -> a.membership().retired()).orElse(false);
            return alreadyRetired ? Retirement.alreadyRetired : Retirement.none;
        }
        if ( ! nodeRepository.nodeResourceLimits().isWithinRealLimits(candidate, cluster)) return Retirement.outsideRealLimits;
        if (violatesParentHostPolicy(candidate)) return Retirement.violatesParentHostPolicy;
        if ( ! hasCompatibleResources(candidate)) return Retirement.incompatibleResources;
        if (candidate.parent.map(node -> node.status().wantToUpgradeFlavor()).orElse(false)) return Retirement.violatesHostFlavorGeneration;
        if (candidate.wantToRetire()) return Retirement.hardRequest;
        if (candidate.preferToRetire() && candidate.replaceableBy(candidates)) return Retirement.softRequest;
        if (violatesExclusivity(candidate) != NodeCandidate.ExclusivityViolation.NONE) return Retirement.violatesExclusivity;
        if (candidate.violatesSpares) return Retirement.violatesSpares;

        var group = candidate.allocation().get().membership().cluster().group();
        if (cluster.isStateful() && group.isPresent() && requested.count().isPresent()) {
            long nodesInGroup = nodes.values().stream().filter(n -> groupOf(n).equals(group) && ! isRetired(n)).count();
            if (nodesInGroup >= requested.groupSize())
                return Retirement.groupSurplus;
        }
        return Retirement.none;
    }
Returns the cause of retirement for given candidate
node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java