root / logic / backend.py @ ac63eb4f
History | View | Annotate | Download (4.2 kB)
1 |
#
|
---|---|
2 |
# Business Logic for communication with the Ganeti backend
|
3 |
#
|
4 |
# Copyright 2010 Greek Research and Technology Network
|
5 |
#
|
6 |
|
7 |
from django.conf import settings |
8 |
from synnefo.db.models import VirtualMachine |
9 |
from synnefo.logic import utils |
10 |
from synnefo.util.rapi import GanetiRapiClient |
11 |
|
12 |
|
13 |
rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO) |
14 |
|
15 |
|
16 |
def process_op_status(vm, jobid, opcode, status, logmsg): |
17 |
"""Process a job progress notification from the backend
|
18 |
|
19 |
Process an incoming message from the backend (currently Ganeti).
|
20 |
Job notifications with a terminating status (sucess, error, or canceled),
|
21 |
also update the operating state of the VM.
|
22 |
|
23 |
"""
|
24 |
if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or |
25 |
status not in [x[0] for x in VirtualMachine.BACKEND_STATUSES]): |
26 |
raise VirtualMachine.InvalidBackendMsgError(opcode, status)
|
27 |
|
28 |
vm.backendjobid = jobid |
29 |
vm.backendjobstatus = status |
30 |
vm.backendopcode = opcode |
31 |
vm.backendlogmsg = logmsg |
32 |
|
33 |
# Notifications of success change the operating state
|
34 |
if status == 'success' and VirtualMachine.OPER_STATE_FROM_OPCODE[opcode] is not None: |
35 |
utils.update_state(vm, VirtualMachine.OPER_STATE_FROM_OPCODE[opcode]) |
36 |
# Set the deleted flag explicitly, to cater for admin-initiated removals
|
37 |
if opcode == 'OP_INSTANCE_REMOVE': |
38 |
vm.deleted = True
|
39 |
|
40 |
# Special case: if OP_INSTANCE_CREATE fails --> ERROR
|
41 |
if status in ('canceled', 'error') and opcode == 'OP_INSTANCE_CREATE': |
42 |
utils.update_state(vm, 'ERROR')
|
43 |
# Any other notification of failure leaves the operating state unchanged
|
44 |
|
45 |
vm.save() |
46 |
|
47 |
|
48 |
def process_net_status(vm, nics): |
49 |
"""Process a net status notification from the backend
|
50 |
|
51 |
Process an incoming message from the Ganeti backend,
|
52 |
detailing the NIC configuration of a VM instance.
|
53 |
|
54 |
Update the state of the VM in the DB accordingly.
|
55 |
|
56 |
"""
|
57 |
|
58 |
# For the time being, we can only update the ipfour field,
|
59 |
# based on the IPv4 address of the first NIC
|
60 |
if len(nics) > 0: |
61 |
ipv4 = nics[0]['ip'] |
62 |
if ipv4 == '': |
63 |
ipv4 = '0.0.0.0'
|
64 |
vm.ipfour = ipv4 |
65 |
vm.save() |
66 |
|
67 |
|
68 |
def start_action(vm, action): |
69 |
"""Update the state of a VM when a new action is initiated."""
|
70 |
if not action in [x[0] for x in VirtualMachine.ACTIONS]: |
71 |
raise VirtualMachine.InvalidActionError(action)
|
72 |
|
73 |
# No actions to deleted and no actions beside destroy to suspended VMs
|
74 |
if vm.deleted:
|
75 |
raise VirtualMachine.DeletedError
|
76 |
|
77 |
# No actions to machines being built. They may be destroyed, however.
|
78 |
if vm.operstate == 'BUILD' and action != 'DESTROY': |
79 |
raise VirtualMachine.BuildingError
|
80 |
|
81 |
vm.action = action |
82 |
vm.backendjobid = None
|
83 |
vm.backendopcode = None
|
84 |
vm.backendjobstatus = None
|
85 |
vm.backendlogmsg = None
|
86 |
|
87 |
# Update the relevant flags if the VM is being suspended or destroyed
|
88 |
if action == "DESTROY": |
89 |
vm.deleted = True
|
90 |
elif action == "SUSPEND": |
91 |
vm.suspended = True
|
92 |
elif action == "START": |
93 |
vm.suspended = False
|
94 |
vm.save() |
95 |
|
96 |
|
97 |
def create_instance(vm, flavor, password): |
98 |
# FIXME: `password` must be passed to the Ganeti OS provider via CreateInstance()
|
99 |
return rapi.CreateInstance(
|
100 |
mode='create',
|
101 |
name=vm.backend_id, |
102 |
disk_template='plain',
|
103 |
disks=[{"size": 2000}], #FIXME: Always ask for a 2GB disk for now |
104 |
nics=[{}], |
105 |
os='debootstrap+default', #TODO: select OS from imageRef |
106 |
ip_check=False,
|
107 |
name_check=False,
|
108 |
pnode=rapi.GetNodes()[0], #TODO: verify if this is necessary |
109 |
dry_run=settings.TEST, |
110 |
beparams=dict(auto_balance=True, vcpus=flavor.cpu, memory=flavor.ram)) |
111 |
|
112 |
def delete_instance(vm): |
113 |
start_action(vm, 'DESTROY')
|
114 |
rapi.DeleteInstance(vm.backend_id) |
115 |
|
116 |
|
117 |
def reboot_instance(vm, reboot_type): |
118 |
assert reboot_type in ('soft', 'hard') |
119 |
rapi.RebootInstance(vm.backend_id, reboot_type) |
120 |
|
121 |
|
122 |
def startup_instance(vm): |
123 |
start_action(vm, 'START')
|
124 |
rapi.StartupInstance(vm.backend_id) |
125 |
|
126 |
|
127 |
def shutdown_instance(vm): |
128 |
start_action(vm, 'STOP')
|
129 |
rapi.ShutdownInstance(vm.backend_id) |
130 |
|
131 |
|
132 |
def get_instance_console(vm): |
133 |
return rapi.GetInstanceConsole(vm.backend_id)
|