Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / backend.py @ 1ad47ca5

History | View | Annotate | Download (25.4 kB)

1 adee02b8 Giorgos Verigakis
# Copyright 2011 GRNET S.A. All rights reserved.
2 37ca953f Christodoulos Psaltis
#
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 37ca953f Christodoulos Psaltis
#
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 37ca953f Christodoulos Psaltis
#
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 37ca953f Christodoulos Psaltis
#
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 37ca953f Christodoulos Psaltis
#
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 2b1db26f Giorgos Verigakis
import json
35 2b1db26f Giorgos Verigakis
36 529178b1 Giorgos Verigakis
from django.conf import settings
37 207b70d5 Giorgos Verigakis
from django.db import transaction
38 1a894bfe Christos Stavrakakis
from datetime import datetime
39 207b70d5 Giorgos Verigakis
40 22ee6892 Christos Stavrakakis
from synnefo.db.models import (Backend, VirtualMachine, Network,
41 3524241a Christos Stavrakakis
                               BackendNetwork, BACKEND_STATUSES,
42 4d5d0b9c Christos Stavrakakis
                               pooled_rapi_client, BridgePoolTable,
43 4d5d0b9c Christos Stavrakakis
                               MacPrefixPoolTable, VirtualMachineDiagnostic)
44 03992c72 Christos Stavrakakis
from synnefo.logic import utils
45 cb4eee84 Christos Stavrakakis
from synnefo import quotas
46 b7d38981 Dimitris Aragiorgis
from synnefo.api.util import release_resource
47 529178b1 Giorgos Verigakis
48 3524241a Christos Stavrakakis
from logging import getLogger
49 3524241a Christos Stavrakakis
log = getLogger(__name__)
50 9e98ba3c Giorgos Verigakis
51 529178b1 Giorgos Verigakis
52 efff6193 Giorgos Verigakis
_firewall_tags = {
53 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
54 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
55 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
56 efff6193 Giorgos Verigakis
57 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
58 efff6193 Giorgos Verigakis
59 02feca11 Vassilios Karakoidas
60 cb4eee84 Christos Stavrakakis
@quotas.uses_commission
61 093f9c53 Vangelis Koukis
@transaction.commit_on_success
62 cb4eee84 Christos Stavrakakis
def process_op_status(serials, vm, etime, jobid, opcode, status, logmsg):
63 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
64 02feca11 Vassilios Karakoidas

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

69 02feca11 Vassilios Karakoidas
    """
70 41303ed0 Vangelis Koukis
    # See #1492, #1031, #1111 why this line has been removed
71 41303ed0 Vangelis Koukis
    #if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
72 fd65ab41 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
73 02feca11 Vassilios Karakoidas
        raise VirtualMachine.InvalidBackendMsgError(opcode, status)
74 02feca11 Vassilios Karakoidas
75 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = jobid
76 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = status
77 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = opcode
78 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = logmsg
79 02feca11 Vassilios Karakoidas
80 02feca11 Vassilios Karakoidas
    # Notifications of success change the operating state
81 41303ed0 Vangelis Koukis
    state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode, None)
82 41303ed0 Vangelis Koukis
    if status == 'success' and state_for_success is not None:
83 c4ce868e Christos Stavrakakis
        vm.operstate = state_for_success
84 685b219e Vangelis Koukis
85 91954b45 Christos Stavrakakis
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
86 cb4eee84 Christos Stavrakakis
    if opcode == 'OP_INSTANCE_CREATE' and status in ('canceled', 'error'):
87 c4ce868e Christos Stavrakakis
        vm.operstate = 'ERROR'
88 c4ce868e Christos Stavrakakis
        vm.backendtime = etime
89 cb4eee84 Christos Stavrakakis
    elif opcode == 'OP_INSTANCE_REMOVE':
90 e97288bc Christos Stavrakakis
        # Set the deleted flag explicitly, cater for admin-initiated removals
91 e97288bc Christos Stavrakakis
        # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR,
92 e97288bc Christos Stavrakakis
        # when no instance exists at the Ganeti backend.
93 e97288bc Christos Stavrakakis
        # See ticket #799 for all the details.
94 e97288bc Christos Stavrakakis
        #
95 e97288bc Christos Stavrakakis
        if status == 'success' or (status == 'error' and
96 e97288bc Christos Stavrakakis
                                   vm.operstate == 'ERROR'):
97 cb4eee84 Christos Stavrakakis
            # Issue commission
98 cb4eee84 Christos Stavrakakis
            serial = quotas.issue_vm_commission(vm.userid, vm.flavor,
99 cb4eee84 Christos Stavrakakis
                                                delete=True)
100 cb4eee84 Christos Stavrakakis
            serials.append(serial)
101 cb4eee84 Christos Stavrakakis
            vm.serial = serial
102 cb4eee84 Christos Stavrakakis
            serial.accepted = True
103 cb4eee84 Christos Stavrakakis
            serial.save()
104 e97288bc Christos Stavrakakis
            release_instance_nics(vm)
105 e97288bc Christos Stavrakakis
            vm.nics.all().delete()
106 e97288bc Christos Stavrakakis
            vm.deleted = True
107 e97288bc Christos Stavrakakis
            vm.operstate = state_for_success
108 e97288bc Christos Stavrakakis
            vm.backendtime = etime
109 093f9c53 Vangelis Koukis
110 c4ce868e Christos Stavrakakis
    # Update backendtime only for jobs that have been successfully completed,
111 c4ce868e Christos Stavrakakis
    # since only these jobs update the state of the VM. Else a "race condition"
112 c4ce868e Christos Stavrakakis
    # may occur when a successful job (e.g. OP_INSTANCE_REMOVE) completes
113 c4ce868e Christos Stavrakakis
    # before an error job and messages arrive in reversed order.
114 c4ce868e Christos Stavrakakis
    if status == 'success':
115 c4ce868e Christos Stavrakakis
        vm.backendtime = etime
116 02feca11 Vassilios Karakoidas
117 02feca11 Vassilios Karakoidas
    vm.save()
118 22e52ede Vassilios Karakoidas
119 ad2d6807 Vangelis Koukis
120 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
121 c4e55622 Christos Stavrakakis
def process_net_status(vm, etime, nics):
122 ad2d6807 Vangelis Koukis
    """Process a net status notification from the backend
123 ad2d6807 Vangelis Koukis

124 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
125 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
126 ad2d6807 Vangelis Koukis

127 ad2d6807 Vangelis Koukis
    Update the state of the VM in the DB accordingly.
128 ad2d6807 Vangelis Koukis
    """
129 37ca953f Christodoulos Psaltis
130 b578d9e7 Christos Stavrakakis
    ganeti_nics = process_ganeti_nics(nics)
131 b578d9e7 Christos Stavrakakis
    if not nics_changed(vm.nics.order_by('index'), ganeti_nics):
132 b578d9e7 Christos Stavrakakis
        log.debug("NICs for VM %s have not changed", vm)
133 b578d9e7 Christos Stavrakakis
134 fdc94944 Christos Stavrakakis
    release_instance_nics(vm)
135 77f0fa63 Christos Stavrakakis
136 b578d9e7 Christos Stavrakakis
    for nic in ganeti_nics:
137 b578d9e7 Christos Stavrakakis
        ipv4 = nic.get('ipv4', '')
138 f45a7ac4 Christos Stavrakakis
        net = nic['network']
139 b578d9e7 Christos Stavrakakis
        if ipv4:
140 b578d9e7 Christos Stavrakakis
            net.reserve_address(ipv4)
141 b578d9e7 Christos Stavrakakis
142 b578d9e7 Christos Stavrakakis
        nic['dirty'] = False
143 b578d9e7 Christos Stavrakakis
        vm.nics.create(**nic)
144 b578d9e7 Christos Stavrakakis
        # Dummy save the network, because UI uses changed-since for VMs
145 b578d9e7 Christos Stavrakakis
        # and Networks in order to show the VM NICs
146 b578d9e7 Christos Stavrakakis
        net.save()
147 b578d9e7 Christos Stavrakakis
148 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
149 b578d9e7 Christos Stavrakakis
    vm.save()
150 b578d9e7 Christos Stavrakakis
151 b578d9e7 Christos Stavrakakis
152 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
153 b578d9e7 Christos Stavrakakis
    """Process NIC dict from ganeti hooks."""
154 b578d9e7 Christos Stavrakakis
    new_nics = []
155 b578d9e7 Christos Stavrakakis
    for i, new_nic in enumerate(ganeti_nics):
156 77f0fa63 Christos Stavrakakis
        network = new_nic.get('network', '')
157 22ee6892 Christos Stavrakakis
        n = str(network)
158 77f0fa63 Christos Stavrakakis
        pk = utils.id_from_network_name(n)
159 77f0fa63 Christos Stavrakakis
160 fdc94944 Christos Stavrakakis
        net = Network.objects.get(pk=pk)
161 77f0fa63 Christos Stavrakakis
162 77f0fa63 Christos Stavrakakis
        # Get the new nic info
163 77f0fa63 Christos Stavrakakis
        mac = new_nic.get('mac', '')
164 77f0fa63 Christos Stavrakakis
        ipv4 = new_nic.get('ip', '')
165 77f0fa63 Christos Stavrakakis
        ipv6 = new_nic.get('ipv6', '')
166 9afeb669 Kostas Papadimitriou
167 77f0fa63 Christos Stavrakakis
        firewall = new_nic.get('firewall', '')
168 658a825a Giorgos Verigakis
        firewall_profile = _reverse_tags.get(firewall, '')
169 658a825a Giorgos Verigakis
        if not firewall_profile and net.public:
170 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
171 9afeb669 Kostas Papadimitriou
172 b578d9e7 Christos Stavrakakis
        nic = {
173 cc92b70f Christos Stavrakakis
            'index': i,
174 cc92b70f Christos Stavrakakis
            'network': net,
175 cc92b70f Christos Stavrakakis
            'mac': mac,
176 cc92b70f Christos Stavrakakis
            'ipv4': ipv4,
177 cc92b70f Christos Stavrakakis
            'ipv6': ipv6,
178 cc92b70f Christos Stavrakakis
            'firewall_profile': firewall_profile}
179 b578d9e7 Christos Stavrakakis
180 b578d9e7 Christos Stavrakakis
        new_nics.append(nic)
181 b578d9e7 Christos Stavrakakis
    return new_nics
182 b578d9e7 Christos Stavrakakis
183 b578d9e7 Christos Stavrakakis
184 b578d9e7 Christos Stavrakakis
def nics_changed(old_nics, new_nics):
185 b578d9e7 Christos Stavrakakis
    """Return True if NICs have changed in any way."""
186 b578d9e7 Christos Stavrakakis
    if len(old_nics) != len(new_nics):
187 b578d9e7 Christos Stavrakakis
        return True
188 b578d9e7 Christos Stavrakakis
    for old_nic, new_nic in zip(old_nics, new_nics):
189 cc92b70f Christos Stavrakakis
        if not (old_nic.ipv4 == new_nic['ipv4'] and
190 cc92b70f Christos Stavrakakis
                old_nic.ipv6 == new_nic['ipv6'] and
191 cc92b70f Christos Stavrakakis
                old_nic.mac == new_nic['mac'] and
192 cc92b70f Christos Stavrakakis
                old_nic.firewall_profile == new_nic['firewall_profile'] and
193 cc92b70f Christos Stavrakakis
                old_nic.index == new_nic['index'] and
194 b578d9e7 Christos Stavrakakis
                old_nic.network == new_nic['network']):
195 b578d9e7 Christos Stavrakakis
            return True
196 b578d9e7 Christos Stavrakakis
    return False
197 22ee6892 Christos Stavrakakis
198 22ee6892 Christos Stavrakakis
199 bd392934 Christos Stavrakakis
def release_instance_nics(vm):
200 bd392934 Christos Stavrakakis
    for nic in vm.nics.all():
201 40ef487d Christos Stavrakakis
        net = nic.network
202 fa454545 Christos Stavrakakis
        if nic.ipv4:
203 40ef487d Christos Stavrakakis
            net.release_address(nic.ipv4)
204 bd392934 Christos Stavrakakis
        nic.delete()
205 40ef487d Christos Stavrakakis
        net.save()
206 bd392934 Christos Stavrakakis
207 bd392934 Christos Stavrakakis
208 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
209 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
210 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
211 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
212 22ee6892 Christos Stavrakakis
213 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
214 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
215 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
216 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
217 22ee6892 Christos Stavrakakis
218 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
219 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
220 22ee6892 Christos Stavrakakis
    if status == 'success' and state_for_success is not None:
221 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
222 22ee6892 Christos Stavrakakis
223 e6f6627c Christos Stavrakakis
    if status in ('canceled', 'error') and opcode == 'OP_NETWORK_ADD':
224 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
225 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
226 22ee6892 Christos Stavrakakis
227 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
228 e97288bc Christos Stavrakakis
        if status == 'success' or (status == 'error' and
229 e97288bc Christos Stavrakakis
                                   back_network.operstate == 'ERROR'):
230 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
231 e97288bc Christos Stavrakakis
            back_network.deleted = True
232 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
233 22ee6892 Christos Stavrakakis
234 e97288bc Christos Stavrakakis
    if status == 'success':
235 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
236 22ee6892 Christos Stavrakakis
    back_network.save()
237 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
238 4d5d0b9c Christos Stavrakakis
    update_network_state(back_network.network)
239 fd2bdbb2 Christos Stavrakakis
240 fd2bdbb2 Christos Stavrakakis
241 cb4eee84 Christos Stavrakakis
@quotas.uses_commission
242 cb4eee84 Christos Stavrakakis
def update_network_state(serials, network):
243 cb4eee84 Christos Stavrakakis
    old_state = network.state
244 cb4eee84 Christos Stavrakakis
245 3f77e63d Christos Stavrakakis
    backend_states = [s.operstate for s in
246 cc92b70f Christos Stavrakakis
                      network.backend_networks.filter(backend__offline=False)]
247 cb4eee84 Christos Stavrakakis
    if not backend_states:
248 cb4eee84 Christos Stavrakakis
        network.state = 'PENDING'
249 cb4eee84 Christos Stavrakakis
        network.save()
250 cb4eee84 Christos Stavrakakis
        return
251 cb4eee84 Christos Stavrakakis
252 cb4eee84 Christos Stavrakakis
    all_equal = len(set(backend_states)) <= 1
253 cb4eee84 Christos Stavrakakis
    network.state = all_equal and backend_states[0] or 'PENDING'
254 cb4eee84 Christos Stavrakakis
    # Release the resources on the deletion of the Network
255 cb4eee84 Christos Stavrakakis
    if old_state != 'DELETED' and network.state == 'DELETED':
256 cb4eee84 Christos Stavrakakis
        log.info("Network %r deleted. Releasing link %r mac_prefix %r",
257 cb4eee84 Christos Stavrakakis
                 network.id, network.mac_prefix, network.link)
258 cb4eee84 Christos Stavrakakis
        network.deleted = True
259 b7d38981 Dimitris Aragiorgis
        if network.mac_prefix:
260 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["mac_prefix"] == "pool":
261 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="mac_prefix",
262 b7d38981 Dimitris Aragiorgis
                                 value=network.mac_prefix)
263 b7d38981 Dimitris Aragiorgis
        if network.link:
264 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["link"] == "pool":
265 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="bridge", value=network.link)
266 cb4eee84 Christos Stavrakakis
267 cb4eee84 Christos Stavrakakis
        # Issue commission
268 cb4eee84 Christos Stavrakakis
        serial = quotas.issue_network_commission(network.userid, delete=True)
269 cb4eee84 Christos Stavrakakis
        serials.append(serial)
270 cb4eee84 Christos Stavrakakis
        network.serial = serial
271 cb4eee84 Christos Stavrakakis
        serial.accepted = True
272 cb4eee84 Christos Stavrakakis
        serial.save()
273 cb4eee84 Christos Stavrakakis
    network.save()
274 cb4eee84 Christos Stavrakakis
275 cb4eee84 Christos Stavrakakis
276 fd2bdbb2 Christos Stavrakakis
@transaction.commit_on_success
277 fd2bdbb2 Christos Stavrakakis
def process_network_modify(back_network, etime, jobid, opcode, status,
278 fd2bdbb2 Christos Stavrakakis
                           add_reserved_ips, remove_reserved_ips):
279 fd2bdbb2 Christos Stavrakakis
    assert (opcode == "OP_NETWORK_SET_PARAMS")
280 fd2bdbb2 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
281 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
282 fd2bdbb2 Christos Stavrakakis
283 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobid = jobid
284 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobstatus = status
285 fd2bdbb2 Christos Stavrakakis
    back_network.opcode = opcode
286 fd2bdbb2 Christos Stavrakakis
287 fd2bdbb2 Christos Stavrakakis
    if add_reserved_ips or remove_reserved_ips:
288 fd2bdbb2 Christos Stavrakakis
        net = back_network.network
289 fd2bdbb2 Christos Stavrakakis
        pool = net.get_pool()
290 fd2bdbb2 Christos Stavrakakis
        if add_reserved_ips:
291 fd2bdbb2 Christos Stavrakakis
            for ip in add_reserved_ips:
292 fd2bdbb2 Christos Stavrakakis
                pool.reserve(ip, external=True)
293 fd2bdbb2 Christos Stavrakakis
        if remove_reserved_ips:
294 fd2bdbb2 Christos Stavrakakis
            for ip in remove_reserved_ips:
295 fd2bdbb2 Christos Stavrakakis
                pool.put(ip, external=True)
296 fd2bdbb2 Christos Stavrakakis
        pool.save()
297 fd2bdbb2 Christos Stavrakakis
298 fd2bdbb2 Christos Stavrakakis
    if status == 'success':
299 fd2bdbb2 Christos Stavrakakis
        back_network.backendtime = etime
300 fd2bdbb2 Christos Stavrakakis
    back_network.save()
301 ad2d6807 Vangelis Koukis
302 c25cc9ec Vangelis Koukis
303 9068cd85 Georgios Gousios
@transaction.commit_on_success
304 0827883e Nikos Skalkotos
def process_create_progress(vm, etime, progress):
305 9068cd85 Georgios Gousios
306 0827883e Nikos Skalkotos
    percentage = int(progress)
307 9068cd85 Georgios Gousios
308 af90d919 Vangelis Koukis
    # The percentage may exceed 100%, due to the way
309 0827883e Nikos Skalkotos
    # snf-image:copy-progress tracks bytes read by image handling processes
310 af90d919 Vangelis Koukis
    percentage = 100 if percentage > 100 else percentage
311 af90d919 Vangelis Koukis
    if percentage < 0:
312 af90d919 Vangelis Koukis
        raise ValueError("Percentage cannot be negative")
313 9068cd85 Georgios Gousios
314 7ec9558b Vangelis Koukis
    # FIXME: log a warning here, see #1033
315 7ec9558b Vangelis Koukis
#   if last_update > percentage:
316 7ec9558b Vangelis Koukis
#       raise ValueError("Build percentage should increase monotonically " \
317 7ec9558b Vangelis Koukis
#                        "(old = %d, new = %d)" % (last_update, percentage))
318 9068cd85 Georgios Gousios
319 c25cc9ec Vangelis Koukis
    # This assumes that no message of type 'ganeti-create-progress' is going to
320 c25cc9ec Vangelis Koukis
    # arrive once OP_INSTANCE_CREATE has succeeded for a Ganeti instance and
321 c25cc9ec Vangelis Koukis
    # the instance is STARTED.  What if the two messages are processed by two
322 c25cc9ec Vangelis Koukis
    # separate dispatcher threads, and the 'ganeti-op-status' message for
323 c25cc9ec Vangelis Koukis
    # successful creation gets processed before the 'ganeti-create-progress'
324 c25cc9ec Vangelis Koukis
    # message? [vkoukis]
325 c25cc9ec Vangelis Koukis
    #
326 c25cc9ec Vangelis Koukis
    #if not vm.operstate == 'BUILD':
327 c25cc9ec Vangelis Koukis
    #    raise VirtualMachine.IllegalState("VM is not in building state")
328 9068cd85 Georgios Gousios
329 c25cc9ec Vangelis Koukis
    vm.buildpercentage = percentage
330 c4e55622 Christos Stavrakakis
    vm.backendtime = etime
331 9068cd85 Georgios Gousios
    vm.save()
332 ad2d6807 Vangelis Koukis
333 c25cc9ec Vangelis Koukis
334 7d43565f Kostas Papadimitriou
def create_instance_diagnostic(vm, message, source, level="DEBUG", etime=None,
335 cc92b70f Christos Stavrakakis
                               details=None):
336 7d43565f Kostas Papadimitriou
    """
337 7d43565f Kostas Papadimitriou
    Create virtual machine instance diagnostic entry.
338 7d43565f Kostas Papadimitriou

339 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
340 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
341 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
342 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
343 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
344 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
345 7d43565f Kostas Papadimitriou
    """
346 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
347 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
348 cc92b70f Christos Stavrakakis
                                                   message=message,
349 cc92b70f Christos Stavrakakis
                                                   details=details)
350 7d43565f Kostas Papadimitriou
351 7d43565f Kostas Papadimitriou
352 79b7dbb7 Christos Stavrakakis
def create_instance(vm, public_nic, flavor, image, password=None):
353 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
354 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
355 7f691719 Christos Stavrakakis

356 3a9b3cde Giorgos Verigakis
        metadata value should be a dictionary.
357 3a9b3cde Giorgos Verigakis
    """
358 864bed43 Christos Stavrakakis
359 1c382247 Vangelis Koukis
    # Handle arguments to CreateInstance() as a dictionary,
360 1c382247 Vangelis Koukis
    # initialize it based on a deployment-specific value.
361 1c382247 Vangelis Koukis
    # This enables the administrator to override deployment-specific
362 c25cc9ec Vangelis Koukis
    # arguments, such as the disk template to use, name of os provider
363 1c382247 Vangelis Koukis
    # and hypervisor-specific parameters at will (see Synnefo #785, #835).
364 1c382247 Vangelis Koukis
    #
365 1c382247 Vangelis Koukis
    kw = settings.GANETI_CREATEINSTANCE_KWARGS
366 1c382247 Vangelis Koukis
    kw['mode'] = 'create'
367 924d8085 Christos Stavrakakis
    kw['name'] = vm.backend_vm_id
368 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
369 296682fe Kostas Papadimitriou
370 2a599282 Christos Stavrakakis
    kw['disk_template'] = flavor.disk_template
371 d7841399 Christos Stavrakakis
    kw['disks'] = [{"size": flavor.disk * 1024}]
372 006c6249 Christos Stavrakakis
    provider = flavor.disk_provider
373 296682fe Kostas Papadimitriou
    if provider:
374 296682fe Kostas Papadimitriou
        kw['disks'][0]['provider'] = provider
375 296682fe Kostas Papadimitriou
376 27d6d48d Stratos Psomadakis
        if provider == 'vlmc':
377 2a599282 Christos Stavrakakis
            kw['disks'][0]['origin'] = flavor.disk_origin
378 296682fe Kostas Papadimitriou
379 adc46059 Christos Stavrakakis
    kw['nics'] = [public_nic]
380 341c818e Dimitris Aragiorgis
    if settings.GANETI_USE_HOTPLUG:
381 341c818e Dimitris Aragiorgis
        kw['hotplug'] = True
382 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
383 1c382247 Vangelis Koukis
    # kw['os'] = settings.GANETI_OS_PROVIDER
384 1c382247 Vangelis Koukis
    kw['ip_check'] = False
385 1c382247 Vangelis Koukis
    kw['name_check'] = False
386 79b7dbb7 Christos Stavrakakis
387 1c382247 Vangelis Koukis
    # Do not specific a node explicitly, have
388 1c382247 Vangelis Koukis
    # Ganeti use an iallocator instead
389 f8d1eaf4 Kostas Papadimitriou
    #kw['pnode'] = rapi.GetNodes()[0]
390 79b7dbb7 Christos Stavrakakis
391 1c382247 Vangelis Koukis
    kw['dry_run'] = settings.TEST
392 41303ed0 Vangelis Koukis
393 2b1db26f Giorgos Verigakis
    kw['beparams'] = {
394 cc92b70f Christos Stavrakakis
        'auto_balance': True,
395 cc92b70f Christos Stavrakakis
        'vcpus': flavor.cpu,
396 cc92b70f Christos Stavrakakis
        'memory': flavor.ram}
397 79b7dbb7 Christos Stavrakakis
398 e3b5be49 Giorgos Verigakis
    kw['osparams'] = {
399 79b7dbb7 Christos Stavrakakis
        'config_url': vm.config_url,
400 79b7dbb7 Christos Stavrakakis
        # Store image id and format to Ganeti
401 2a599282 Christos Stavrakakis
        'img_id': image['backend_id'],
402 3a9b3cde Giorgos Verigakis
        'img_format': image['format']}
403 d1eaa651 Christos Stavrakakis
404 79b7dbb7 Christos Stavrakakis
    if password:
405 79b7dbb7 Christos Stavrakakis
        # Only for admin created VMs !!
406 79b7dbb7 Christos Stavrakakis
        kw['osparams']['img_passwd'] = password
407 d1eaa651 Christos Stavrakakis
408 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
409 1c382247 Vangelis Koukis
    # kw['hvparams'] = dict(serial_console=False)
410 79b7dbb7 Christos Stavrakakis
411 6afeb85d Christos Stavrakakis
    log.debug("Creating instance %s", utils.hide_pass(kw))
412 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
413 3524241a Christos Stavrakakis
        return client.CreateInstance(**kw)
414 f533f224 Vangelis Koukis
415 529178b1 Giorgos Verigakis
416 529178b1 Giorgos Verigakis
def delete_instance(vm):
417 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
418 3524241a Christos Stavrakakis
        return client.DeleteInstance(vm.backend_vm_id, dry_run=settings.TEST)
419 529178b1 Giorgos Verigakis
420 ad2d6807 Vangelis Koukis
421 529178b1 Giorgos Verigakis
def reboot_instance(vm, reboot_type):
422 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
423 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
424 bf5c82dc Christos Stavrakakis
        return client.RebootInstance(vm.backend_vm_id, reboot_type,
425 bf5c82dc Christos Stavrakakis
                                     dry_run=settings.TEST)
426 529178b1 Giorgos Verigakis
427 ad2d6807 Vangelis Koukis
428 529178b1 Giorgos Verigakis
def startup_instance(vm):
429 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
430 3524241a Christos Stavrakakis
        return client.StartupInstance(vm.backend_vm_id, dry_run=settings.TEST)
431 529178b1 Giorgos Verigakis
432 ad2d6807 Vangelis Koukis
433 529178b1 Giorgos Verigakis
def shutdown_instance(vm):
434 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
435 3524241a Christos Stavrakakis
        return client.ShutdownInstance(vm.backend_vm_id, dry_run=settings.TEST)
436 529178b1 Giorgos Verigakis
437 ad2d6807 Vangelis Koukis
438 529178b1 Giorgos Verigakis
def get_instance_console(vm):
439 71099804 Vangelis Koukis
    # RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
440 71099804 Vangelis Koukis
    # which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
441 71099804 Vangelis Koukis
    # useless (see #783).
442 71099804 Vangelis Koukis
    #
443 71099804 Vangelis Koukis
    # Until this is fixed on the Ganeti side, construct a console info reply
444 71099804 Vangelis Koukis
    # directly.
445 9afeb669 Kostas Papadimitriou
    #
446 71099804 Vangelis Koukis
    # WARNING: This assumes that VNC runs on port network_port on
447 71099804 Vangelis Koukis
    #          the instance's primary node, and is probably
448 71099804 Vangelis Koukis
    #          hypervisor-specific.
449 71099804 Vangelis Koukis
    #
450 bf5c82dc Christos Stavrakakis
    log.debug("Getting console for vm %s", vm)
451 bf5c82dc Christos Stavrakakis
452 71099804 Vangelis Koukis
    console = {}
453 71099804 Vangelis Koukis
    console['kind'] = 'vnc'
454 3524241a Christos Stavrakakis
455 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
456 3524241a Christos Stavrakakis
        i = client.GetInstance(vm.backend_vm_id)
457 3524241a Christos Stavrakakis
458 71099804 Vangelis Koukis
    if i['hvparams']['serial_console']:
459 71099804 Vangelis Koukis
        raise Exception("hv parameter serial_console cannot be true")
460 71099804 Vangelis Koukis
    console['host'] = i['pnode']
461 71099804 Vangelis Koukis
    console['port'] = i['network_port']
462 9afeb669 Kostas Papadimitriou
463 71099804 Vangelis Koukis
    return console
464 604b2bf8 Georgios Gousios
465 604b2bf8 Georgios Gousios
466 3524241a Christos Stavrakakis
def get_instance_info(vm):
467 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
468 3524241a Christos Stavrakakis
        return client.GetInstanceInfo(vm.backend_vm_id)
469 f533f224 Vangelis Koukis
470 c25cc9ec Vangelis Koukis
471 3524241a Christos Stavrakakis
def create_network(network, backends=None, connect=True):
472 3524241a Christos Stavrakakis
    """Create and connect a network."""
473 3524241a Christos Stavrakakis
    if not backends:
474 3524241a Christos Stavrakakis
        backends = Backend.objects.exclude(offline=True)
475 64938cb0 Giorgos Verigakis
476 bf5c82dc Christos Stavrakakis
    log.debug("Creating network %s in backends %s", network, backends)
477 c25cc9ec Vangelis Koukis
478 3524241a Christos Stavrakakis
    for backend in backends:
479 3524241a Christos Stavrakakis
        create_jobID = _create_network(network, backend)
480 3524241a Christos Stavrakakis
        if connect:
481 3524241a Christos Stavrakakis
            connect_network(network, backend, create_jobID)
482 37ca953f Christodoulos Psaltis
483 64938cb0 Giorgos Verigakis
484 3524241a Christos Stavrakakis
def _create_network(network, backend):
485 3524241a Christos Stavrakakis
    """Create a network."""
486 c25cc9ec Vangelis Koukis
487 22ee6892 Christos Stavrakakis
    network_type = network.public and 'public' or 'private'
488 22ee6892 Christos Stavrakakis
489 22ee6892 Christos Stavrakakis
    tags = network.backend_tag
490 22ee6892 Christos Stavrakakis
    if network.dhcp:
491 22ee6892 Christos Stavrakakis
        tags.append('nfdhcpd')
492 2d762302 Dimitris Aragiorgis
493 2d762302 Dimitris Aragiorgis
    if network.public:
494 2d762302 Dimitris Aragiorgis
        conflicts_check = True
495 2d762302 Dimitris Aragiorgis
    else:
496 2d762302 Dimitris Aragiorgis
        conflicts_check = False
497 22ee6892 Christos Stavrakakis
498 3524241a Christos Stavrakakis
    try:
499 3524241a Christos Stavrakakis
        bn = BackendNetwork.objects.get(network=network, backend=backend)
500 3524241a Christos Stavrakakis
        mac_prefix = bn.mac_prefix
501 3524241a Christos Stavrakakis
    except BackendNetwork.DoesNotExist:
502 cc92b70f Christos Stavrakakis
        raise Exception("BackendNetwork for network '%s' in backend '%s'"
503 3524241a Christos Stavrakakis
                        " does not exist" % (network.id, backend.id))
504 3524241a Christos Stavrakakis
505 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
506 3524241a Christos Stavrakakis
        return client.CreateNetwork(network_name=network.backend_id,
507 3524241a Christos Stavrakakis
                                    network=network.subnet,
508 6cc3a31c Christos Stavrakakis
                                    network6=network.subnet6,
509 3524241a Christos Stavrakakis
                                    gateway=network.gateway,
510 6cc3a31c Christos Stavrakakis
                                    gateway6=network.gateway6,
511 3524241a Christos Stavrakakis
                                    network_type=network_type,
512 3524241a Christos Stavrakakis
                                    mac_prefix=mac_prefix,
513 2d762302 Dimitris Aragiorgis
                                    conflicts_check=conflicts_check,
514 3524241a Christos Stavrakakis
                                    tags=tags)
515 3524241a Christos Stavrakakis
516 3524241a Christos Stavrakakis
517 3524241a Christos Stavrakakis
def connect_network(network, backend, depend_job=None, group=None):
518 3524241a Christos Stavrakakis
    """Connect a network to nodegroups."""
519 bf5c82dc Christos Stavrakakis
    log.debug("Connecting network %s to backend %s", network, backend)
520 bf5c82dc Christos Stavrakakis
521 2d762302 Dimitris Aragiorgis
    if network.public:
522 2d762302 Dimitris Aragiorgis
        conflicts_check = True
523 2d762302 Dimitris Aragiorgis
    else:
524 2d762302 Dimitris Aragiorgis
        conflicts_check = False
525 2d762302 Dimitris Aragiorgis
526 122c4019 Christos Stavrakakis
    depend_jobs = [depend_job] if depend_job else []
527 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
528 3524241a Christos Stavrakakis
        if group:
529 b7d38981 Dimitris Aragiorgis
            client.ConnectNetwork(network.backend_id, group, network.mode,
530 2d762302 Dimitris Aragiorgis
                                  network.link, conflicts_check, depend_jobs)
531 3524241a Christos Stavrakakis
        else:
532 3524241a Christos Stavrakakis
            for group in client.GetGroups():
533 b7d38981 Dimitris Aragiorgis
                client.ConnectNetwork(network.backend_id, group, network.mode,
534 2d762302 Dimitris Aragiorgis
                                      network.link, conflicts_check,
535 2d762302 Dimitris Aragiorgis
                                      depend_jobs)
536 22ee6892 Christos Stavrakakis
537 22ee6892 Christos Stavrakakis
538 3524241a Christos Stavrakakis
def delete_network(network, backends=None, disconnect=True):
539 22ee6892 Christos Stavrakakis
    if not backends:
540 22ee6892 Christos Stavrakakis
        backends = Backend.objects.exclude(offline=True)
541 22ee6892 Christos Stavrakakis
542 bf5c82dc Christos Stavrakakis
    log.debug("Deleting network %s from backends %s", network, backends)
543 bf5c82dc Christos Stavrakakis
544 22ee6892 Christos Stavrakakis
    for backend in backends:
545 3524241a Christos Stavrakakis
        disconnect_jobIDs = []
546 3524241a Christos Stavrakakis
        if disconnect:
547 3524241a Christos Stavrakakis
            disconnect_jobIDs = disconnect_network(network, backend)
548 3524241a Christos Stavrakakis
        _delete_network(network, backend, disconnect_jobIDs)
549 22ee6892 Christos Stavrakakis
550 22ee6892 Christos Stavrakakis
551 3524241a Christos Stavrakakis
def _delete_network(network, backend, depend_jobs=[]):
552 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
553 3524241a Christos Stavrakakis
        return client.DeleteNetwork(network.backend_id, depend_jobs)
554 22ee6892 Christos Stavrakakis
555 22ee6892 Christos Stavrakakis
556 3524241a Christos Stavrakakis
def disconnect_network(network, backend, group=None):
557 bf5c82dc Christos Stavrakakis
    log.debug("Disconnecting network %s to backend %s", network, backend)
558 22ee6892 Christos Stavrakakis
559 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
560 3524241a Christos Stavrakakis
        if group:
561 3524241a Christos Stavrakakis
            return [client.DisconnectNetwork(network.backend_id, group)]
562 3524241a Christos Stavrakakis
        else:
563 3524241a Christos Stavrakakis
            jobs = []
564 3524241a Christos Stavrakakis
            for group in client.GetGroups():
565 3524241a Christos Stavrakakis
                job = client.DisconnectNetwork(network.backend_id, group)
566 3524241a Christos Stavrakakis
                jobs.append(job)
567 3524241a Christos Stavrakakis
            return jobs
568 36f4cb29 Christos Stavrakakis
569 36f4cb29 Christos Stavrakakis
570 87920bc3 Christos Stavrakakis
def connect_to_network(vm, network, address=None):
571 3524241a Christos Stavrakakis
    nic = {'ip': address, 'network': network.backend_id}
572 22ee6892 Christos Stavrakakis
573 bf5c82dc Christos Stavrakakis
    log.debug("Connecting vm %s to network %s(%s)", vm, network, address)
574 22ee6892 Christos Stavrakakis
575 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
576 3524241a Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, nics=[('add',  nic)],
577 3524241a Christos Stavrakakis
                                     hotplug=settings.GANETI_USE_HOTPLUG,
578 3524241a Christos Stavrakakis
                                     dry_run=settings.TEST)
579 22ee6892 Christos Stavrakakis
580 22ee6892 Christos Stavrakakis
581 3524241a Christos Stavrakakis
def disconnect_from_network(vm, nic):
582 3524241a Christos Stavrakakis
    op = [('remove', nic.index, {})]
583 22ee6892 Christos Stavrakakis
584 bf5c82dc Christos Stavrakakis
    log.debug("Removing nic of VM %s, with index %s", vm, str(nic.index))
585 22ee6892 Christos Stavrakakis
586 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
587 3524241a Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, nics=op,
588 3524241a Christos Stavrakakis
                                     hotplug=settings.GANETI_USE_HOTPLUG,
589 3524241a Christos Stavrakakis
                                     dry_run=settings.TEST)
590 91826390 Giorgos Verigakis
591 c25cc9ec Vangelis Koukis
592 91826390 Giorgos Verigakis
def set_firewall_profile(vm, profile):
593 26563957 Giorgos Verigakis
    try:
594 26563957 Giorgos Verigakis
        tag = _firewall_tags[profile]
595 26563957 Giorgos Verigakis
    except KeyError:
596 91826390 Giorgos Verigakis
        raise ValueError("Unsopported Firewall Profile: %s" % profile)
597 37ca953f Christodoulos Psaltis
598 bf5c82dc Christos Stavrakakis
    log.debug("Setting tag of VM %s to %s", vm, profile)
599 bf5c82dc Christos Stavrakakis
600 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
601 3524241a Christos Stavrakakis
        # Delete all firewall tags
602 3524241a Christos Stavrakakis
        for t in _firewall_tags.values():
603 3524241a Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, [t],
604 3524241a Christos Stavrakakis
                                      dry_run=settings.TEST)
605 37ca953f Christodoulos Psaltis
606 3524241a Christos Stavrakakis
        client.AddInstanceTags(vm.backend_vm_id, [tag], dry_run=settings.TEST)
607 9afeb669 Kostas Papadimitriou
608 3524241a Christos Stavrakakis
        # XXX NOP ModifyInstance call to force process_net_status to run
609 3524241a Christos Stavrakakis
        # on the dispatcher
610 cc92b70f Christos Stavrakakis
        os_name = settings.GANETI_CREATEINSTANCE_KWARGS['os']
611 3524241a Christos Stavrakakis
        client.ModifyInstance(vm.backend_vm_id,
612 cc92b70f Christos Stavrakakis
                              os_name=os_name)
613 5eedb0e4 Vangelis Koukis
614 41303ed0 Vangelis Koukis
615 f5b4f2a3 Christos Stavrakakis
def get_ganeti_instances(backend=None, bulk=False):
616 3524241a Christos Stavrakakis
    instances = []
617 3524241a Christos Stavrakakis
    for backend in get_backends(backend):
618 3524241a Christos Stavrakakis
        with pooled_rapi_client(backend) as client:
619 3524241a Christos Stavrakakis
            instances.append(client.GetInstances(bulk=bulk))
620 3524241a Christos Stavrakakis
621 3524241a Christos Stavrakakis
    return reduce(list.__add__, instances, [])
622 f5b4f2a3 Christos Stavrakakis
623 f5b4f2a3 Christos Stavrakakis
624 f5b4f2a3 Christos Stavrakakis
def get_ganeti_nodes(backend=None, bulk=False):
625 3524241a Christos Stavrakakis
    nodes = []
626 3524241a Christos Stavrakakis
    for backend in get_backends(backend):
627 3524241a Christos Stavrakakis
        with pooled_rapi_client(backend) as client:
628 3524241a Christos Stavrakakis
            nodes.append(client.GetNodes(bulk=bulk))
629 3524241a Christos Stavrakakis
630 3524241a Christos Stavrakakis
    return reduce(list.__add__, nodes, [])
631 f5b4f2a3 Christos Stavrakakis
632 f5b4f2a3 Christos Stavrakakis
633 f5b4f2a3 Christos Stavrakakis
def get_ganeti_jobs(backend=None, bulk=False):
634 3524241a Christos Stavrakakis
    jobs = []
635 3524241a Christos Stavrakakis
    for backend in get_backends(backend):
636 3524241a Christos Stavrakakis
        with pooled_rapi_client(backend) as client:
637 3524241a Christos Stavrakakis
            jobs.append(client.GetJobs(bulk=bulk))
638 3524241a Christos Stavrakakis
    return reduce(list.__add__, jobs, [])
639 f5b4f2a3 Christos Stavrakakis
640 f5b4f2a3 Christos Stavrakakis
##
641 f5b4f2a3 Christos Stavrakakis
##
642 f5b4f2a3 Christos Stavrakakis
##
643 1a894bfe Christos Stavrakakis
644 1a894bfe Christos Stavrakakis
645 f5b4f2a3 Christos Stavrakakis
def get_backends(backend=None):
646 f5b4f2a3 Christos Stavrakakis
    if backend:
647 c414bc87 Christos Stavrakakis
        if backend.offline:
648 c414bc87 Christos Stavrakakis
            return []
649 f5b4f2a3 Christos Stavrakakis
        return [backend]
650 cc7c0f44 Christos Stavrakakis
    return Backend.objects.filter(offline=False)
651 f5b4f2a3 Christos Stavrakakis
652 17852fe9 Giorgos Verigakis
653 1a894bfe Christos Stavrakakis
def get_physical_resources(backend):
654 1a894bfe Christos Stavrakakis
    """ Get the physical resources of a backend.
655 1a894bfe Christos Stavrakakis

656 1a894bfe Christos Stavrakakis
    Get the resources of a backend as reported by the backend (not the db).
657 41303ed0 Vangelis Koukis

658 1a894bfe Christos Stavrakakis
    """
659 1a894bfe Christos Stavrakakis
    nodes = get_ganeti_nodes(backend, bulk=True)
660 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
661 1a894bfe Christos Stavrakakis
    res = {}
662 1a894bfe Christos Stavrakakis
    for a in attr:
663 1a894bfe Christos Stavrakakis
        res[a] = 0
664 1a894bfe Christos Stavrakakis
    for n in nodes:
665 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
666 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
667 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
668 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
669 1a894bfe Christos Stavrakakis
            for a in attr:
670 1a894bfe Christos Stavrakakis
                res[a] += int(n[a])
671 1a894bfe Christos Stavrakakis
    return res
672 1a894bfe Christos Stavrakakis
673 1a894bfe Christos Stavrakakis
674 1a894bfe Christos Stavrakakis
def update_resources(backend, resources=None):
675 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
676 1a894bfe Christos Stavrakakis

677 1a894bfe Christos Stavrakakis
    """
678 17852fe9 Giorgos Verigakis
679 1a894bfe Christos Stavrakakis
    if not resources:
680 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
681 41303ed0 Vangelis Koukis
682 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
683 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
684 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
685 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
686 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
687 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
688 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
689 1a894bfe Christos Stavrakakis
    backend.save()
690 1a894bfe Christos Stavrakakis
691 1a894bfe Christos Stavrakakis
692 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
693 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
694 1a894bfe Christos Stavrakakis

695 1a894bfe Christos Stavrakakis
    Get the used memory of a backend. Note: This is different for
696 1a894bfe Christos Stavrakakis
    the real memory used, due to kvm's memory de-duplication.
697 1a894bfe Christos Stavrakakis

698 1a894bfe Christos Stavrakakis
    """
699 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
700 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
701 1a894bfe Christos Stavrakakis
    mem = 0
702 1a894bfe Christos Stavrakakis
    for i in instances:
703 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
704 1a894bfe Christos Stavrakakis
    return mem
705 b3d28af2 Christos Stavrakakis
706 b3d28af2 Christos Stavrakakis
##
707 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
708 b3d28af2 Christos Stavrakakis
##
709 b3d28af2 Christos Stavrakakis
710 b3d28af2 Christos Stavrakakis
711 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
712 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
713 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
714 b3d28af2 Christos Stavrakakis
        return result
715 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
716 b3d28af2 Christos Stavrakakis
    return result
717 b3d28af2 Christos Stavrakakis
718 b3d28af2 Christos Stavrakakis
719 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
720 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
721 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
722 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
723 3524241a Christos Stavrakakis
    return result
724 b3d28af2 Christos Stavrakakis
725 b3d28af2 Christos Stavrakakis
726 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
727 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
728 3524241a Christos Stavrakakis
        for group in client.GetGroups():
729 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
730 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
731 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
732 3524241a Christos Stavrakakis
            if result[0] != 'success':
733 3524241a Christos Stavrakakis
                return result
734 b3d28af2 Christos Stavrakakis
735 b3d28af2 Christos Stavrakakis
    return result
736 b3d28af2 Christos Stavrakakis
737 b3d28af2 Christos Stavrakakis
738 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
739 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
740 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
741 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
742 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
743 cc92b70f Christos Stavrakakis
                                         [result], None)
744 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
745 b3d28af2 Christos Stavrakakis
746 b3d28af2 Christos Stavrakakis
    if status == 'success':
747 b3d28af2 Christos Stavrakakis
        return (status, None)
748 b3d28af2 Christos Stavrakakis
    else:
749 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
750 b3d28af2 Christos Stavrakakis
        return (status, error)