Statistics
| Branch: | Tag: | Revision:

root / logic / backend.py @ 583bfaa0

History | View | Annotate | Download (9.9 kB)

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

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

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

90 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
91 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
92 ad2d6807 Vangelis Koukis

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