scheduler.py 2.78 KB
Newer Older
1 2
from logging import getLogger

3 4
from django.db.models import Sum

5 6
logger = getLogger(__name__)

7 8

class NotEnoughMemoryException(Exception):
9

10 11 12 13 14 15 16 17
    def __init__(self, message=None):
        if message is None:
            message = "No node has enough memory to accomodate the guest."

        Exception.__init__(self, message)


class TraitsUnsatisfiableException(Exception):
18

19 20 21 22 23 24 25
    def __init__(self, message=None):
        if message is None:
            message = "No node can satisfy all required traits of the guest."

        Exception.__init__(self, message)


26
def select_node(instance, nodes):
27
    ''' Select a node for hosting an instance based on its requirements.
tarokkk committed
28
    '''
29
    # check required traits
30
    nodes = [n for n in nodes
31 32
             if n.enabled and n.online
             and has_traits(instance.req_traits.all(), n)]
33
    if not nodes:
34
        logger.warning('select_node: no usable node for %s', unicode(instance))
35 36 37 38 39
        raise TraitsUnsatisfiableException()

    # check required RAM
    nodes = [n for n in nodes if has_enough_ram(instance.ram_size, n)]
    if not nodes:
40
        logger.warning('select_node: no enough RAM for %s', unicode(instance))
41 42 43 44
        raise NotEnoughMemoryException()

    # sort nodes first by processor usage, then priority
    nodes.sort(key=lambda n: n.priority, reverse=True)
45
    nodes.sort(key=free_cpu_time, reverse=True)
46
    result = nodes[0]
47

48 49
    logger.info('select_node: %s for %s', unicode(result), unicode(instance))
    return result
50 51 52 53 54 55 56 57 58 59 60 61 62


def has_traits(traits, node):
    """True, if the node has all specified traits; otherwise, false.
    """
    traits = set(traits)
    return traits.issubset(node.traits.all())


def has_enough_ram(ram_size, node):
    """True, if the node has enough memory to accomodate a guest requiring
       ram_size mebibytes of memory; otherwise, false.
    """
63 64 65 66
    try:
        total = node.ram_size
        used = (node.ram_usage / 100) * total
        unused = total - used
67

68 69 70
        overcommit = node.ram_size_with_overcommit
        reserved = node.instance_set.aggregate(r=Sum('ram_size'))['r'] or 0
        free = overcommit - reserved
71

72 73 74 75 76
        return ram_size < unused and ram_size < free
    except TypeError as e:
        logger.warning('Got incorrect monitoring data for node %s. %s',
                       unicode(node), unicode(e))
        return False
77 78


79 80
def free_cpu_time(node):
    """Get an indicator number for idle processor time on the node.
81

82
    Higher values indicate more idle time.
83
    """
84 85 86 87 88 89 90 91 92
    try:
        activity = node.cpu_usage / 100
        inactivity = 1 - activity
        cores = node.num_cores
        return cores * inactivity
    except TypeError as e:
        logger.warning('Got incorrect monitoring data for node %s. %s',
                       unicode(node), unicode(e))
        return False  # monitoring data is incorrect