Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / backend_allocator.py @ cc7c0f44

History | View | Annotate | Download (4.1 kB)

1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
#
7
#   1. Redistributions of source code must retain the above copyright
8
#      notice, this list of conditions and the following disclaimer.
9
#
10
#  2. Redistributions in binary form must reproduce the above copyright
11
#     notice, this list of conditions and the following disclaimer in the
12
#     documentation and/or other materials provided with the distribution.
13
#
14
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
# SUCH DAMAGE.
25
#
26
# The views and conclusions contained in the software and documentation are
27
# those of the authors and should not be interpreted as representing official
28
# policies, either expressed or implied, of GRNET S.A.
29

    
30
import datetime
31
from django.utils import importlib
32

    
33
from synnefo import settings
34
from synnefo.db.models import Backend
35
from synnefo.logic.backend import update_resources
36

    
37

    
38
class BackendAllocator():
39
    """Wrapper class for instance allocation.
40

41
    """
42
    def __init__(self):
43
        self.strategy_mod =\
44
            importlib.import_module(settings.BACKEND_ALLOCATOR_MODULE)
45

    
46
    def allocate(self, flavor):
47
        # Get the size of the vm
48
        disk = flavor_disk(flavor)
49
        ram = flavor.ram
50
        cpu = flavor.cpu
51
        vm = {'ram': ram, 'disk': disk, 'cpu': cpu}
52

    
53
        # Refresh backends, if needed
54
        refresh_backends_stats()
55

    
56
        # Get available backends
57
        available_backends = get_available_backends()
58

    
59
        if not available_backends:
60
            return None
61

    
62
        # Find the best backend to host the vm, based on the allocation
63
        # strategy
64
        backend_id = self.strategy_mod.allocate(available_backends, vm)
65

    
66
        backend = Backend.objects.get(id=backend_id)
67
        # Reduce the free resources of the selected backend by the size of
68
        # the vm
69
        reduce_backend_resources(backend, vm)
70

    
71
        return backend
72

    
73

    
74
def get_available_backends():
75
    """Get available backends from db.
76

77
    """
78
    attrs = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
79
    backends = {}
80
    for b in Backend.objects.filter(drained=False, offline=False):
81
        backend = {}
82
        for a in attrs:
83
            backend[a] = getattr(b, a)
84
        backends[b.id] = backend
85
    return backends
86

    
87

    
88
def flavor_disk(flavor):
89
    """ Get flavor's 'real' disk size
90

91
    """
92
    if flavor.disk_template == 'drbd':
93
        return flavor.disk * 1024 * 2
94
    else:
95
        return flavor.disk * 1024
96

    
97

    
98
def reduce_backend_resources(backend, vm):
99
    """ Conservatively update the resources of a backend.
100

101
    Reduce the free resources of the backend by the size of the of the vm that
102
    will host. This is an underestimation of the backend capabilities.
103

104
    """
105

    
106
    new_mfree = backend.mfree - vm['ram']
107
    new_dfree = backend.dfree - vm['disk']
108
    backend.mfree = 0 if new_mfree < 0 else new_mfree
109
    backend.dfree = 0 if new_dfree < 0 else new_dfree
110
    backend.pinst_cnt += 1
111

    
112
    backend.save()
113

    
114

    
115
def refresh_backends_stats():
116
    """ Refresh the statistics of the backends.
117

118
    Set db backend state to the actual state of the backend, if
119
    BACKEND_REFRESH_MIN time has passed.
120

121
    """
122

    
123
    now = datetime.datetime.now()
124
    delta = datetime.timedelta(minutes=settings.BACKEND_REFRESH_MIN)
125
    for b in Backend.objects.filter(drained=False, offline=False):
126
        if now > b.updated + delta:
127
            update_resources(b)