Statistics
| Branch: | Tag: | Revision:

root / logic / backend.py @ 746c6bf4

History | View | Annotate | Download (9.2 kB)

1 adee02b8 Giorgos Verigakis
# Copyright 2011 GRNET S.A. All rights reserved.
2 adee02b8 Giorgos Verigakis
# 
3 adee02b8 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 adee02b8 Giorgos Verigakis
# without modification, are permitted provided that the following
5 adee02b8 Giorgos Verigakis
# conditions are met:
6 adee02b8 Giorgos Verigakis
# 
7 adee02b8 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 adee02b8 Giorgos Verigakis
#      disclaimer.
10 adee02b8 Giorgos Verigakis
# 
11 adee02b8 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 adee02b8 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 adee02b8 Giorgos Verigakis
#      provided with the distribution.
15 adee02b8 Giorgos Verigakis
# 
16 adee02b8 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 adee02b8 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 adee02b8 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 adee02b8 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 adee02b8 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 adee02b8 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 adee02b8 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 adee02b8 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 adee02b8 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 adee02b8 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 adee02b8 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 adee02b8 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 adee02b8 Giorgos Verigakis
# 
29 adee02b8 Giorgos Verigakis
# The views and conclusions contained in the software and
30 adee02b8 Giorgos Verigakis
# documentation are those of the authors and should not be
31 adee02b8 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 adee02b8 Giorgos Verigakis
# or implied, of GRNET S.A.
33 02feca11 Vassilios Karakoidas
34 529178b1 Giorgos Verigakis
from django.conf import settings
35 207b70d5 Giorgos Verigakis
from django.db import transaction
36 207b70d5 Giorgos Verigakis
37 adee02b8 Giorgos Verigakis
from synnefo.db.models import (VirtualMachine, Network, NetworkInterface,
38 adee02b8 Giorgos Verigakis
                                NetworkLink)
39 234f8b07 Vangelis Koukis
from synnefo.logic import utils
40 529178b1 Giorgos Verigakis
from synnefo.util.rapi import GanetiRapiClient
41 529178b1 Giorgos Verigakis
42 529178b1 Giorgos Verigakis
43 529178b1 Giorgos Verigakis
rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO)
44 529178b1 Giorgos Verigakis
45 02feca11 Vassilios Karakoidas
46 ad2d6807 Vangelis Koukis
def process_op_status(vm, jobid, opcode, status, logmsg):
47 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
48 02feca11 Vassilios Karakoidas

49 02feca11 Vassilios Karakoidas
    Process an incoming message from the backend (currently Ganeti).
50 02feca11 Vassilios Karakoidas
    Job notifications with a terminating status (sucess, error, or canceled),
51 02feca11 Vassilios Karakoidas
    also update the operating state of the VM.
52 02feca11 Vassilios Karakoidas

53 02feca11 Vassilios Karakoidas
    """
54 02feca11 Vassilios Karakoidas
    if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
55 02feca11 Vassilios Karakoidas
       status not in [x[0] for x in VirtualMachine.BACKEND_STATUSES]):
56 02feca11 Vassilios Karakoidas
        raise VirtualMachine.InvalidBackendMsgError(opcode, status)
57 02feca11 Vassilios Karakoidas
58 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = jobid
59 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = status
60 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = opcode
61 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = logmsg
62 02feca11 Vassilios Karakoidas
63 02feca11 Vassilios Karakoidas
    # Notifications of success change the operating state
64 ac63eb4f Vangelis Koukis
    if status == 'success' and VirtualMachine.OPER_STATE_FROM_OPCODE[opcode] is not None:
65 234f8b07 Vangelis Koukis
        utils.update_state(vm, VirtualMachine.OPER_STATE_FROM_OPCODE[opcode])
66 685b219e Vangelis Koukis
        # Set the deleted flag explicitly, to cater for admin-initiated removals
67 685b219e Vangelis Koukis
        if opcode == 'OP_INSTANCE_REMOVE':
68 685b219e Vangelis Koukis
            vm.deleted = True
69 685b219e Vangelis Koukis
70 685b219e Vangelis Koukis
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
71 02feca11 Vassilios Karakoidas
    if status in ('canceled', 'error') and opcode == 'OP_INSTANCE_CREATE':
72 234f8b07 Vangelis Koukis
        utils.update_state(vm, 'ERROR')
73 02feca11 Vassilios Karakoidas
    # Any other notification of failure leaves the operating state unchanged
74 02feca11 Vassilios Karakoidas
75 02feca11 Vassilios Karakoidas
    vm.save()
76 22e52ede Vassilios Karakoidas
77 ad2d6807 Vangelis Koukis
78 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
79 ad2d6807 Vangelis Koukis
def process_net_status(vm, nics):
80 ad2d6807 Vangelis Koukis
    """Process a net status notification from the backend
81 ad2d6807 Vangelis Koukis

82 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
83 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
84 ad2d6807 Vangelis Koukis

85 ad2d6807 Vangelis Koukis
    Update the state of the VM in the DB accordingly.
86 ad2d6807 Vangelis Koukis
    """
87 64938cb0 Giorgos Verigakis
    
88 64938cb0 Giorgos Verigakis
    vm.nics.all().delete()
89 64938cb0 Giorgos Verigakis
    for i, nic in enumerate(nics):
90 64938cb0 Giorgos Verigakis
        if i == 0:
91 ca792e04 Giorgos Verigakis
            net = Network.objects.get(public=True)
92 64938cb0 Giorgos Verigakis
        else:
93 a1dccf43 Vangelis Koukis
            try:
94 a1dccf43 Vangelis Koukis
                link = NetworkLink.objects.get(name=nic['link'])
95 a1dccf43 Vangelis Koukis
            except NetworkLink.DoesNotExist:
96 a1dccf43 Vangelis Koukis
                # Cannot find an instance of NetworkLink for
97 a1dccf43 Vangelis Koukis
                # the link attribute specified in the notification
98 a1dccf43 Vangelis Koukis
                raise NetworkLink.DoesNotExist("Cannot find a NetworkLink "
99 a1dccf43 Vangelis Koukis
                    "object for link='%s'" % nic['link'])
100 64938cb0 Giorgos Verigakis
            net = link.network
101 a1dccf43 Vangelis Koukis
            if net is None:
102 a1dccf43 Vangelis Koukis
                raise Network.DoesNotExist("NetworkLink for link='%s' not "
103 a1dccf43 Vangelis Koukis
                    "associated with an existing Network instance." %
104 a1dccf43 Vangelis Koukis
                    nic['link'])
105 a1dccf43 Vangelis Koukis
106 64938cb0 Giorgos Verigakis
        vm.nics.create(
107 64938cb0 Giorgos Verigakis
            network=net,
108 64938cb0 Giorgos Verigakis
            index=i,
109 64938cb0 Giorgos Verigakis
            mac=nic.get('mac', ''),
110 746c6bf4 Vangelis Koukis
            ipv4=nic.get('ip', ''),
111 746c6bf4 Vangelis Koukis
            ipv6=nic.get('ipv6',''))
112 ad2d6807 Vangelis Koukis
    vm.save()
113 ad2d6807 Vangelis Koukis
114 ad2d6807 Vangelis Koukis
115 22e52ede Vassilios Karakoidas
def start_action(vm, action):
116 22e52ede Vassilios Karakoidas
    """Update the state of a VM when a new action is initiated."""
117 22e52ede Vassilios Karakoidas
    if not action in [x[0] for x in VirtualMachine.ACTIONS]:
118 22e52ede Vassilios Karakoidas
        raise VirtualMachine.InvalidActionError(action)
119 22e52ede Vassilios Karakoidas
120 22e52ede Vassilios Karakoidas
    # No actions to deleted and no actions beside destroy to suspended VMs
121 22e52ede Vassilios Karakoidas
    if vm.deleted:
122 5231a38a Giorgos Verigakis
        raise VirtualMachine.DeletedError
123 30f3b5e5 Vangelis Koukis
   
124 30f3b5e5 Vangelis Koukis
    # No actions to machines being built. They may be destroyed, however.
125 30f3b5e5 Vangelis Koukis
    if vm.operstate == 'BUILD' and action != 'DESTROY':
126 5231a38a Giorgos Verigakis
        raise VirtualMachine.BuildingError
127 5231a38a Giorgos Verigakis
    
128 dfd19c2d Vassilios Karakoidas
    vm.action = action
129 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = None
130 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = None
131 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = None
132 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = None
133 22e52ede Vassilios Karakoidas
134 22e52ede Vassilios Karakoidas
    # Update the relevant flags if the VM is being suspended or destroyed
135 22e52ede Vassilios Karakoidas
    if action == "DESTROY":
136 22e52ede Vassilios Karakoidas
        vm.deleted = True
137 22e52ede Vassilios Karakoidas
    elif action == "SUSPEND":
138 22e52ede Vassilios Karakoidas
        vm.suspended = True
139 22e52ede Vassilios Karakoidas
    elif action == "START":
140 22e52ede Vassilios Karakoidas
        vm.suspended = False
141 22e52ede Vassilios Karakoidas
    vm.save()
142 529178b1 Giorgos Verigakis
143 ad2d6807 Vangelis Koukis
144 1ef58e5b Constantinos Venetsanopoulos
def create_instance(vm, flavor, image, password):
145 64938cb0 Giorgos Verigakis
    
146 64938cb0 Giorgos Verigakis
    nic = {'ip': 'pool', 'mode': 'routed', 'link': settings.GANETI_PUBLIC_LINK}
147 64938cb0 Giorgos Verigakis
    
148 529178b1 Giorgos Verigakis
    return rapi.CreateInstance(
149 529178b1 Giorgos Verigakis
        mode='create',
150 12fd6446 Giorgos Verigakis
        name=vm.backend_id,
151 529178b1 Giorgos Verigakis
        disk_template='plain',
152 f533f224 Vangelis Koukis
        disks=[{"size": 4000}],     #FIXME: Always ask for a 4GB disk for now
153 64938cb0 Giorgos Verigakis
        nics=[nic],
154 1ef58e5b Constantinos Venetsanopoulos
        os='image+default',
155 529178b1 Giorgos Verigakis
        ip_check=False,
156 529178b1 Giorgos Verigakis
        name_check=False,
157 f533f224 Vangelis Koukis
        pnode=rapi.GetNodes()[0],   #TODO: use a Ganeti iallocator instead
158 529178b1 Giorgos Verigakis
        dry_run=settings.TEST,
159 1ef58e5b Constantinos Venetsanopoulos
        beparams=dict(auto_balance=True, vcpus=flavor.cpu, memory=flavor.ram),
160 f533f224 Vangelis Koukis
        osparams=dict(img_id=image.backend_id, img_passwd=password,
161 f533f224 Vangelis Koukis
                      img_format=image.format))
162 f533f224 Vangelis Koukis
163 529178b1 Giorgos Verigakis
164 529178b1 Giorgos Verigakis
def delete_instance(vm):
165 529178b1 Giorgos Verigakis
    start_action(vm, 'DESTROY')
166 64938cb0 Giorgos Verigakis
    rapi.DeleteInstance(vm.backend_id, dry_run=settings.TEST)
167 d44c236b Giorgos Verigakis
    vm.nics.all().delete()
168 529178b1 Giorgos Verigakis
169 ad2d6807 Vangelis Koukis
170 529178b1 Giorgos Verigakis
def reboot_instance(vm, reboot_type):
171 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
172 64938cb0 Giorgos Verigakis
    rapi.RebootInstance(vm.backend_id, reboot_type, dry_run=settings.TEST)
173 529178b1 Giorgos Verigakis
174 ad2d6807 Vangelis Koukis
175 529178b1 Giorgos Verigakis
def startup_instance(vm):
176 529178b1 Giorgos Verigakis
    start_action(vm, 'START')
177 64938cb0 Giorgos Verigakis
    rapi.StartupInstance(vm.backend_id, dry_run=settings.TEST)
178 529178b1 Giorgos Verigakis
179 ad2d6807 Vangelis Koukis
180 529178b1 Giorgos Verigakis
def shutdown_instance(vm):
181 529178b1 Giorgos Verigakis
    start_action(vm, 'STOP')
182 64938cb0 Giorgos Verigakis
    rapi.ShutdownInstance(vm.backend_id, dry_run=settings.TEST)
183 529178b1 Giorgos Verigakis
184 ad2d6807 Vangelis Koukis
185 529178b1 Giorgos Verigakis
def get_instance_console(vm):
186 529178b1 Giorgos Verigakis
    return rapi.GetInstanceConsole(vm.backend_id)
187 64938cb0 Giorgos Verigakis
188 64938cb0 Giorgos Verigakis
189 604b2bf8 Georgios Gousios
def request_status_update(vm):
190 604b2bf8 Georgios Gousios
    return rapi.GetInstanceInfo(vm.backend_id)
191 604b2bf8 Georgios Gousios
192 604b2bf8 Georgios Gousios
193 604b2bf8 Georgios Gousios
def get_job_status(jobid):
194 604b2bf8 Georgios Gousios
    return rapi.GetJobStatus(jobid)
195 604b2bf8 Georgios Gousios
196 604b2bf8 Georgios Gousios
197 604b2bf8 Georgios Gousios
def update_status(vm, status):
198 db7a3230 Vangelis Koukis
    utils.update_state(vm, status)
199 f533f224 Vangelis Koukis
200 64938cb0 Giorgos Verigakis
def create_network_link():
201 64938cb0 Giorgos Verigakis
    try:
202 64938cb0 Giorgos Verigakis
        last = NetworkLink.objects.order_by('-index')[0]
203 64938cb0 Giorgos Verigakis
        index = last.index + 1
204 64938cb0 Giorgos Verigakis
    except IndexError:
205 64938cb0 Giorgos Verigakis
        index = 1
206 64938cb0 Giorgos Verigakis
    
207 64938cb0 Giorgos Verigakis
    if index <= settings.GANETI_MAX_LINK_NUMBER:
208 64938cb0 Giorgos Verigakis
        name = '%s%d' % (settings.GANETI_LINK_PREFIX, index)
209 f533f224 Vangelis Koukis
        return NetworkLink.objects.create(index=index, name=name,
210 a191bd4d Giorgos Verigakis
                                            available=True)
211 64938cb0 Giorgos Verigakis
    return None     # All link slots are filled
212 64938cb0 Giorgos Verigakis
213 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
214 a3992827 Giorgos Verigakis
def create_network(name, owner):
215 64938cb0 Giorgos Verigakis
    try:
216 64938cb0 Giorgos Verigakis
        link = NetworkLink.objects.filter(available=True)[0]
217 64938cb0 Giorgos Verigakis
    except IndexError:
218 64938cb0 Giorgos Verigakis
        link = create_network_link()
219 64938cb0 Giorgos Verigakis
        if not link:
220 a3992827 Giorgos Verigakis
            return None
221 a3992827 Giorgos Verigakis
    
222 a3992827 Giorgos Verigakis
    network = Network.objects.create(
223 a3992827 Giorgos Verigakis
        name=name,
224 a3992827 Giorgos Verigakis
        owner=owner,
225 a3992827 Giorgos Verigakis
        state='ACTIVE',
226 a3992827 Giorgos Verigakis
        link=link)
227 a3992827 Giorgos Verigakis
    
228 a3992827 Giorgos Verigakis
    link.network = network
229 64938cb0 Giorgos Verigakis
    link.available = False
230 64938cb0 Giorgos Verigakis
    link.save()
231 a3992827 Giorgos Verigakis
    
232 a3992827 Giorgos Verigakis
    return network
233 64938cb0 Giorgos Verigakis
234 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
235 64938cb0 Giorgos Verigakis
def delete_network(net):
236 64938cb0 Giorgos Verigakis
    link = net.link
237 06a16b24 Giorgos Verigakis
    if link.name != settings.GANETI_NULL_LINK:
238 06a16b24 Giorgos Verigakis
        link.available = True
239 06a16b24 Giorgos Verigakis
        link.network = None
240 06a16b24 Giorgos Verigakis
        link.save()
241 64938cb0 Giorgos Verigakis
    
242 64938cb0 Giorgos Verigakis
    for vm in net.machines.all():
243 64938cb0 Giorgos Verigakis
        disconnect_from_network(vm, net)
244 64938cb0 Giorgos Verigakis
        vm.save()
245 64938cb0 Giorgos Verigakis
    net.state = 'DELETED'
246 64938cb0 Giorgos Verigakis
    net.save()
247 64938cb0 Giorgos Verigakis
248 64938cb0 Giorgos Verigakis
def connect_to_network(vm, net):
249 64938cb0 Giorgos Verigakis
    nic = {'mode': 'bridged', 'link': net.link.name}
250 ca792e04 Giorgos Verigakis
    rapi.ModifyInstance(vm.backend_id,
251 ca792e04 Giorgos Verigakis
        nics=[('add', nic)],
252 ca792e04 Giorgos Verigakis
        dry_run=settings.TEST)
253 64938cb0 Giorgos Verigakis
254 64938cb0 Giorgos Verigakis
def disconnect_from_network(vm, net):
255 ca792e04 Giorgos Verigakis
    nics = vm.nics.filter(network__public=False).order_by('index')
256 ca792e04 Giorgos Verigakis
    new_nics = [nic for nic in nics if nic.network != net]
257 ca792e04 Giorgos Verigakis
    if new_nics == nics:
258 ca792e04 Giorgos Verigakis
        return      # Nothing to remove
259 ca792e04 Giorgos Verigakis
    ops = [('remove', {})]
260 ca792e04 Giorgos Verigakis
    for i, nic in enumerate(new_nics):
261 ca792e04 Giorgos Verigakis
        ops.append((i + 1, {
262 64938cb0 Giorgos Verigakis
            'mode': 'bridged',
263 ca792e04 Giorgos Verigakis
            'link': nic.network.link.name}))
264 ca792e04 Giorgos Verigakis
    rapi.ModifyInstance(vm.backend_id, nics=ops, dry_run=settings.TEST)
265 91826390 Giorgos Verigakis
266 26563957 Giorgos Verigakis
267 26563957 Giorgos Verigakis
_firewall_tags = {
268 26563957 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
269 26563957 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
270 26563957 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
271 26563957 Giorgos Verigakis
272 91826390 Giorgos Verigakis
def set_firewall_profile(vm, profile):
273 26563957 Giorgos Verigakis
    try:
274 26563957 Giorgos Verigakis
        tag = _firewall_tags[profile]
275 26563957 Giorgos Verigakis
    except KeyError:
276 91826390 Giorgos Verigakis
        raise ValueError("Unsopported Firewall Profile: %s" % profile)
277 91826390 Giorgos Verigakis
    
278 26563957 Giorgos Verigakis
    # Delete all firewall tags
279 26563957 Giorgos Verigakis
    rapi.DeleteInstanceTags(vm.backend_id, _firewall_tags.values(),
280 26563957 Giorgos Verigakis
                            dry_run=settings.TEST)
281 26563957 Giorgos Verigakis
    
282 26563957 Giorgos Verigakis
    rapi.AddInstanceTags(vm.backend_id, [tag], dry_run=settings.TEST)