Statistics
| Branch: | Tag: | Revision:

root / logic / backend.py @ 207b70d5

History | View | Annotate | Download (6.2 kB)

1 234f8b07 Vangelis Koukis
#
2 234f8b07 Vangelis Koukis
# Business Logic for communication with the Ganeti backend
3 234f8b07 Vangelis Koukis
#
4 234f8b07 Vangelis Koukis
# Copyright 2010 Greek Research and Technology Network
5 234f8b07 Vangelis Koukis
#
6 02feca11 Vassilios Karakoidas
7 529178b1 Giorgos Verigakis
from django.conf import settings
8 207b70d5 Giorgos Verigakis
from django.db import transaction
9 207b70d5 Giorgos Verigakis
10 64938cb0 Giorgos Verigakis
from synnefo.db.models import VirtualMachine, Network, NetworkLink
11 234f8b07 Vangelis Koukis
from synnefo.logic import utils
12 529178b1 Giorgos Verigakis
from synnefo.util.rapi import GanetiRapiClient
13 529178b1 Giorgos Verigakis
14 529178b1 Giorgos Verigakis
15 529178b1 Giorgos Verigakis
rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO)
16 529178b1 Giorgos Verigakis
17 02feca11 Vassilios Karakoidas
18 ad2d6807 Vangelis Koukis
def process_op_status(vm, jobid, opcode, status, logmsg):
19 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
20 02feca11 Vassilios Karakoidas

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

25 02feca11 Vassilios Karakoidas
    """
26 02feca11 Vassilios Karakoidas
    if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
27 02feca11 Vassilios Karakoidas
       status not in [x[0] for x in VirtualMachine.BACKEND_STATUSES]):
28 02feca11 Vassilios Karakoidas
        raise VirtualMachine.InvalidBackendMsgError(opcode, status)
29 02feca11 Vassilios Karakoidas
30 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = jobid
31 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = status
32 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = opcode
33 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = logmsg
34 02feca11 Vassilios Karakoidas
35 02feca11 Vassilios Karakoidas
    # Notifications of success change the operating state
36 ac63eb4f Vangelis Koukis
    if status == 'success' and VirtualMachine.OPER_STATE_FROM_OPCODE[opcode] is not None:
37 234f8b07 Vangelis Koukis
        utils.update_state(vm, VirtualMachine.OPER_STATE_FROM_OPCODE[opcode])
38 685b219e Vangelis Koukis
        # Set the deleted flag explicitly, to cater for admin-initiated removals
39 685b219e Vangelis Koukis
        if opcode == 'OP_INSTANCE_REMOVE':
40 685b219e Vangelis Koukis
            vm.deleted = True
41 685b219e Vangelis Koukis
42 685b219e Vangelis Koukis
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
43 02feca11 Vassilios Karakoidas
    if status in ('canceled', 'error') and opcode == 'OP_INSTANCE_CREATE':
44 234f8b07 Vangelis Koukis
        utils.update_state(vm, 'ERROR')
45 02feca11 Vassilios Karakoidas
    # Any other notification of failure leaves the operating state unchanged
46 02feca11 Vassilios Karakoidas
47 02feca11 Vassilios Karakoidas
    vm.save()
48 22e52ede Vassilios Karakoidas
49 ad2d6807 Vangelis Koukis
50 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
51 ad2d6807 Vangelis Koukis
def process_net_status(vm, nics):
52 ad2d6807 Vangelis Koukis
    """Process a net status notification from the backend
53 ad2d6807 Vangelis Koukis

54 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
55 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
56 ad2d6807 Vangelis Koukis

57 ad2d6807 Vangelis Koukis
    Update the state of the VM in the DB accordingly.
58 ad2d6807 Vangelis Koukis
    """
59 64938cb0 Giorgos Verigakis
    
60 64938cb0 Giorgos Verigakis
    vm.nics.all().delete()
61 64938cb0 Giorgos Verigakis
    for i, nic in enumerate(nics):
62 64938cb0 Giorgos Verigakis
        if i == 0:
63 64938cb0 Giorgos Verigakis
            net = Network.objects.filter(public=True)[0]
64 64938cb0 Giorgos Verigakis
        else:
65 64938cb0 Giorgos Verigakis
            link = NetworkLink.objects.get(name=nic['link'])
66 64938cb0 Giorgos Verigakis
            net = link.network
67 64938cb0 Giorgos Verigakis
        
68 64938cb0 Giorgos Verigakis
        vm.nics.create(
69 64938cb0 Giorgos Verigakis
            network=net,
70 64938cb0 Giorgos Verigakis
            index=i,
71 64938cb0 Giorgos Verigakis
            mac=nic.get('mac', ''),
72 64938cb0 Giorgos Verigakis
            ipv4=nic.get('ip', ''))
73 ad2d6807 Vangelis Koukis
    vm.save()
74 ad2d6807 Vangelis Koukis
75 ad2d6807 Vangelis Koukis
76 22e52ede Vassilios Karakoidas
def start_action(vm, action):
77 22e52ede Vassilios Karakoidas
    """Update the state of a VM when a new action is initiated."""
78 22e52ede Vassilios Karakoidas
    if not action in [x[0] for x in VirtualMachine.ACTIONS]:
79 22e52ede Vassilios Karakoidas
        raise VirtualMachine.InvalidActionError(action)
80 22e52ede Vassilios Karakoidas
81 22e52ede Vassilios Karakoidas
    # No actions to deleted and no actions beside destroy to suspended VMs
82 22e52ede Vassilios Karakoidas
    if vm.deleted:
83 5231a38a Giorgos Verigakis
        raise VirtualMachine.DeletedError
84 30f3b5e5 Vangelis Koukis
   
85 30f3b5e5 Vangelis Koukis
    # No actions to machines being built. They may be destroyed, however.
86 30f3b5e5 Vangelis Koukis
    if vm.operstate == 'BUILD' and action != 'DESTROY':
87 5231a38a Giorgos Verigakis
        raise VirtualMachine.BuildingError
88 5231a38a Giorgos Verigakis
    
89 dfd19c2d Vassilios Karakoidas
    vm.action = action
90 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = None
91 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = None
92 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = None
93 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = None
94 22e52ede Vassilios Karakoidas
95 22e52ede Vassilios Karakoidas
    # Update the relevant flags if the VM is being suspended or destroyed
96 22e52ede Vassilios Karakoidas
    if action == "DESTROY":
97 22e52ede Vassilios Karakoidas
        vm.deleted = True
98 22e52ede Vassilios Karakoidas
    elif action == "SUSPEND":
99 22e52ede Vassilios Karakoidas
        vm.suspended = True
100 22e52ede Vassilios Karakoidas
    elif action == "START":
101 22e52ede Vassilios Karakoidas
        vm.suspended = False
102 22e52ede Vassilios Karakoidas
    vm.save()
103 529178b1 Giorgos Verigakis
104 ad2d6807 Vangelis Koukis
105 529178b1 Giorgos Verigakis
def create_instance(vm, flavor, password):
106 529178b1 Giorgos Verigakis
    # FIXME: `password` must be passed to the Ganeti OS provider via CreateInstance()
107 64938cb0 Giorgos Verigakis
    
108 64938cb0 Giorgos Verigakis
    nic = {'ip': 'pool', 'mode': 'routed', 'link': settings.GANETI_PUBLIC_LINK}
109 64938cb0 Giorgos Verigakis
    
110 529178b1 Giorgos Verigakis
    return rapi.CreateInstance(
111 529178b1 Giorgos Verigakis
        mode='create',
112 12fd6446 Giorgos Verigakis
        name=vm.backend_id,
113 529178b1 Giorgos Verigakis
        disk_template='plain',
114 529178b1 Giorgos Verigakis
        disks=[{"size": 2000}],         #FIXME: Always ask for a 2GB disk for now
115 64938cb0 Giorgos Verigakis
        nics=[nic],
116 529178b1 Giorgos Verigakis
        os='debootstrap+default',       #TODO: select OS from imageRef
117 529178b1 Giorgos Verigakis
        ip_check=False,
118 529178b1 Giorgos Verigakis
        name_check=False,
119 529178b1 Giorgos Verigakis
        pnode=rapi.GetNodes()[0],       #TODO: verify if this is necessary
120 529178b1 Giorgos Verigakis
        dry_run=settings.TEST,
121 529178b1 Giorgos Verigakis
        beparams=dict(auto_balance=True, vcpus=flavor.cpu, memory=flavor.ram))
122 529178b1 Giorgos Verigakis
123 529178b1 Giorgos Verigakis
def delete_instance(vm):
124 529178b1 Giorgos Verigakis
    start_action(vm, 'DESTROY')
125 64938cb0 Giorgos Verigakis
    rapi.DeleteInstance(vm.backend_id, dry_run=settings.TEST)
126 d44c236b Giorgos Verigakis
    vm.nics.all().delete()
127 529178b1 Giorgos Verigakis
128 ad2d6807 Vangelis Koukis
129 529178b1 Giorgos Verigakis
def reboot_instance(vm, reboot_type):
130 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
131 64938cb0 Giorgos Verigakis
    rapi.RebootInstance(vm.backend_id, reboot_type, dry_run=settings.TEST)
132 529178b1 Giorgos Verigakis
133 ad2d6807 Vangelis Koukis
134 529178b1 Giorgos Verigakis
def startup_instance(vm):
135 529178b1 Giorgos Verigakis
    start_action(vm, 'START')
136 64938cb0 Giorgos Verigakis
    rapi.StartupInstance(vm.backend_id, dry_run=settings.TEST)
137 529178b1 Giorgos Verigakis
138 ad2d6807 Vangelis Koukis
139 529178b1 Giorgos Verigakis
def shutdown_instance(vm):
140 529178b1 Giorgos Verigakis
    start_action(vm, 'STOP')
141 64938cb0 Giorgos Verigakis
    rapi.ShutdownInstance(vm.backend_id, dry_run=settings.TEST)
142 529178b1 Giorgos Verigakis
143 ad2d6807 Vangelis Koukis
144 529178b1 Giorgos Verigakis
def get_instance_console(vm):
145 529178b1 Giorgos Verigakis
    return rapi.GetInstanceConsole(vm.backend_id)
146 64938cb0 Giorgos Verigakis
147 64938cb0 Giorgos Verigakis
148 64938cb0 Giorgos Verigakis
def create_network_link():
149 64938cb0 Giorgos Verigakis
    try:
150 64938cb0 Giorgos Verigakis
        last = NetworkLink.objects.order_by('-index')[0]
151 64938cb0 Giorgos Verigakis
        index = last.index + 1
152 64938cb0 Giorgos Verigakis
    except IndexError:
153 64938cb0 Giorgos Verigakis
        index = 1
154 64938cb0 Giorgos Verigakis
    
155 64938cb0 Giorgos Verigakis
    if index <= settings.GANETI_MAX_LINK_NUMBER:
156 64938cb0 Giorgos Verigakis
        name = '%s%d' % (settings.GANETI_LINK_PREFIX, index)
157 64938cb0 Giorgos Verigakis
        return NetworkLink.objects.create(index=index, name=name, available=True)
158 64938cb0 Giorgos Verigakis
    return None     # All link slots are filled
159 64938cb0 Giorgos Verigakis
160 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
161 64938cb0 Giorgos Verigakis
def create_network(net):
162 64938cb0 Giorgos Verigakis
    try:
163 64938cb0 Giorgos Verigakis
        link = NetworkLink.objects.filter(available=True)[0]
164 64938cb0 Giorgos Verigakis
    except IndexError:
165 64938cb0 Giorgos Verigakis
        link = create_network_link()
166 64938cb0 Giorgos Verigakis
        if not link:
167 64938cb0 Giorgos Verigakis
            return False
168 64938cb0 Giorgos Verigakis
    link.network = net
169 64938cb0 Giorgos Verigakis
    link.available = False
170 64938cb0 Giorgos Verigakis
    link.save()
171 64938cb0 Giorgos Verigakis
    return True
172 64938cb0 Giorgos Verigakis
173 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
174 64938cb0 Giorgos Verigakis
def delete_network(net):
175 64938cb0 Giorgos Verigakis
    link = net.link
176 64938cb0 Giorgos Verigakis
    link.available = True
177 64938cb0 Giorgos Verigakis
    link.netowrk = False
178 64938cb0 Giorgos Verigakis
    link.save()
179 64938cb0 Giorgos Verigakis
    
180 64938cb0 Giorgos Verigakis
    for vm in net.machines.all():
181 64938cb0 Giorgos Verigakis
        disconnect_from_network(vm, net)
182 64938cb0 Giorgos Verigakis
        vm.save()
183 64938cb0 Giorgos Verigakis
    net.state = 'DELETED'
184 64938cb0 Giorgos Verigakis
    net.save()
185 64938cb0 Giorgos Verigakis
186 64938cb0 Giorgos Verigakis
def connect_to_network(vm, net):
187 64938cb0 Giorgos Verigakis
    nic = {'mode': 'bridged', 'link': net.link.name}
188 64938cb0 Giorgos Verigakis
    rapi.ModifyInstance(vm.backend_id, nics=[('add', nic)], dry_run=settings.TEST)
189 64938cb0 Giorgos Verigakis
190 64938cb0 Giorgos Verigakis
def disconnect_from_network(vm, net):
191 64938cb0 Giorgos Verigakis
    nics = vm.nics.order_by('index')[1:]    # Skip the public network
192 64938cb0 Giorgos Verigakis
    ops = [('remove', {})] * len(nics)
193 64938cb0 Giorgos Verigakis
    for nic in nics:
194 64938cb0 Giorgos Verigakis
        if nic.network == net:
195 64938cb0 Giorgos Verigakis
            continue
196 64938cb0 Giorgos Verigakis
        ops.append(('add', {
197 64938cb0 Giorgos Verigakis
            'mode': 'bridged',
198 64938cb0 Giorgos Verigakis
            'link': nic.network.link.name,
199 64938cb0 Giorgos Verigakis
            'mac': nic.mac}))
200 64938cb0 Giorgos Verigakis
    for op in ops:
201 64938cb0 Giorgos Verigakis
        rapi.ModifyInstance(vm.backend_id, nics=[op], dry_run=settings.TEST)