Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / backend.py @ 8283d6c1

History | View | Annotate | Download (25.3 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 8528b8ac Christos Stavrakakis
def create_instance(vm, public_nic, flavor, image):
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 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
405 1c382247 Vangelis Koukis
    # kw['hvparams'] = dict(serial_console=False)
406 79b7dbb7 Christos Stavrakakis
407 6afeb85d Christos Stavrakakis
    log.debug("Creating instance %s", utils.hide_pass(kw))
408 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
409 3524241a Christos Stavrakakis
        return client.CreateInstance(**kw)
410 f533f224 Vangelis Koukis
411 529178b1 Giorgos Verigakis
412 529178b1 Giorgos Verigakis
def delete_instance(vm):
413 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
414 3524241a Christos Stavrakakis
        return client.DeleteInstance(vm.backend_vm_id, dry_run=settings.TEST)
415 529178b1 Giorgos Verigakis
416 ad2d6807 Vangelis Koukis
417 529178b1 Giorgos Verigakis
def reboot_instance(vm, reboot_type):
418 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
419 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
420 bf5c82dc Christos Stavrakakis
        return client.RebootInstance(vm.backend_vm_id, reboot_type,
421 bf5c82dc Christos Stavrakakis
                                     dry_run=settings.TEST)
422 529178b1 Giorgos Verigakis
423 ad2d6807 Vangelis Koukis
424 529178b1 Giorgos Verigakis
def startup_instance(vm):
425 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
426 3524241a Christos Stavrakakis
        return client.StartupInstance(vm.backend_vm_id, dry_run=settings.TEST)
427 529178b1 Giorgos Verigakis
428 ad2d6807 Vangelis Koukis
429 529178b1 Giorgos Verigakis
def shutdown_instance(vm):
430 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
431 3524241a Christos Stavrakakis
        return client.ShutdownInstance(vm.backend_vm_id, dry_run=settings.TEST)
432 529178b1 Giorgos Verigakis
433 ad2d6807 Vangelis Koukis
434 529178b1 Giorgos Verigakis
def get_instance_console(vm):
435 71099804 Vangelis Koukis
    # RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
436 71099804 Vangelis Koukis
    # which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
437 71099804 Vangelis Koukis
    # useless (see #783).
438 71099804 Vangelis Koukis
    #
439 71099804 Vangelis Koukis
    # Until this is fixed on the Ganeti side, construct a console info reply
440 71099804 Vangelis Koukis
    # directly.
441 9afeb669 Kostas Papadimitriou
    #
442 71099804 Vangelis Koukis
    # WARNING: This assumes that VNC runs on port network_port on
443 71099804 Vangelis Koukis
    #          the instance's primary node, and is probably
444 71099804 Vangelis Koukis
    #          hypervisor-specific.
445 71099804 Vangelis Koukis
    #
446 bf5c82dc Christos Stavrakakis
    log.debug("Getting console for vm %s", vm)
447 bf5c82dc Christos Stavrakakis
448 71099804 Vangelis Koukis
    console = {}
449 71099804 Vangelis Koukis
    console['kind'] = 'vnc'
450 3524241a Christos Stavrakakis
451 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
452 3524241a Christos Stavrakakis
        i = client.GetInstance(vm.backend_vm_id)
453 3524241a Christos Stavrakakis
454 71099804 Vangelis Koukis
    if i['hvparams']['serial_console']:
455 71099804 Vangelis Koukis
        raise Exception("hv parameter serial_console cannot be true")
456 71099804 Vangelis Koukis
    console['host'] = i['pnode']
457 71099804 Vangelis Koukis
    console['port'] = i['network_port']
458 9afeb669 Kostas Papadimitriou
459 71099804 Vangelis Koukis
    return console
460 604b2bf8 Georgios Gousios
461 604b2bf8 Georgios Gousios
462 3524241a Christos Stavrakakis
def get_instance_info(vm):
463 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
464 3524241a Christos Stavrakakis
        return client.GetInstanceInfo(vm.backend_vm_id)
465 f533f224 Vangelis Koukis
466 c25cc9ec Vangelis Koukis
467 3524241a Christos Stavrakakis
def create_network(network, backends=None, connect=True):
468 3524241a Christos Stavrakakis
    """Create and connect a network."""
469 3524241a Christos Stavrakakis
    if not backends:
470 3524241a Christos Stavrakakis
        backends = Backend.objects.exclude(offline=True)
471 64938cb0 Giorgos Verigakis
472 bf5c82dc Christos Stavrakakis
    log.debug("Creating network %s in backends %s", network, backends)
473 c25cc9ec Vangelis Koukis
474 3524241a Christos Stavrakakis
    for backend in backends:
475 3524241a Christos Stavrakakis
        create_jobID = _create_network(network, backend)
476 3524241a Christos Stavrakakis
        if connect:
477 3524241a Christos Stavrakakis
            connect_network(network, backend, create_jobID)
478 37ca953f Christodoulos Psaltis
479 64938cb0 Giorgos Verigakis
480 3524241a Christos Stavrakakis
def _create_network(network, backend):
481 3524241a Christos Stavrakakis
    """Create a network."""
482 c25cc9ec Vangelis Koukis
483 22ee6892 Christos Stavrakakis
    network_type = network.public and 'public' or 'private'
484 22ee6892 Christos Stavrakakis
485 22ee6892 Christos Stavrakakis
    tags = network.backend_tag
486 22ee6892 Christos Stavrakakis
    if network.dhcp:
487 22ee6892 Christos Stavrakakis
        tags.append('nfdhcpd')
488 2d762302 Dimitris Aragiorgis
489 2d762302 Dimitris Aragiorgis
    if network.public:
490 2d762302 Dimitris Aragiorgis
        conflicts_check = True
491 2d762302 Dimitris Aragiorgis
    else:
492 2d762302 Dimitris Aragiorgis
        conflicts_check = False
493 22ee6892 Christos Stavrakakis
494 3524241a Christos Stavrakakis
    try:
495 3524241a Christos Stavrakakis
        bn = BackendNetwork.objects.get(network=network, backend=backend)
496 3524241a Christos Stavrakakis
        mac_prefix = bn.mac_prefix
497 3524241a Christos Stavrakakis
    except BackendNetwork.DoesNotExist:
498 cc92b70f Christos Stavrakakis
        raise Exception("BackendNetwork for network '%s' in backend '%s'"
499 3524241a Christos Stavrakakis
                        " does not exist" % (network.id, backend.id))
500 3524241a Christos Stavrakakis
501 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
502 3524241a Christos Stavrakakis
        return client.CreateNetwork(network_name=network.backend_id,
503 3524241a Christos Stavrakakis
                                    network=network.subnet,
504 6cc3a31c Christos Stavrakakis
                                    network6=network.subnet6,
505 3524241a Christos Stavrakakis
                                    gateway=network.gateway,
506 6cc3a31c Christos Stavrakakis
                                    gateway6=network.gateway6,
507 3524241a Christos Stavrakakis
                                    network_type=network_type,
508 3524241a Christos Stavrakakis
                                    mac_prefix=mac_prefix,
509 2d762302 Dimitris Aragiorgis
                                    conflicts_check=conflicts_check,
510 3524241a Christos Stavrakakis
                                    tags=tags)
511 3524241a Christos Stavrakakis
512 3524241a Christos Stavrakakis
513 3524241a Christos Stavrakakis
def connect_network(network, backend, depend_job=None, group=None):
514 3524241a Christos Stavrakakis
    """Connect a network to nodegroups."""
515 bf5c82dc Christos Stavrakakis
    log.debug("Connecting network %s to backend %s", network, backend)
516 bf5c82dc Christos Stavrakakis
517 2d762302 Dimitris Aragiorgis
    if network.public:
518 2d762302 Dimitris Aragiorgis
        conflicts_check = True
519 2d762302 Dimitris Aragiorgis
    else:
520 2d762302 Dimitris Aragiorgis
        conflicts_check = False
521 2d762302 Dimitris Aragiorgis
522 122c4019 Christos Stavrakakis
    depend_jobs = [depend_job] if depend_job else []
523 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
524 3524241a Christos Stavrakakis
        if group:
525 b7d38981 Dimitris Aragiorgis
            client.ConnectNetwork(network.backend_id, group, network.mode,
526 2d762302 Dimitris Aragiorgis
                                  network.link, conflicts_check, depend_jobs)
527 3524241a Christos Stavrakakis
        else:
528 3524241a Christos Stavrakakis
            for group in client.GetGroups():
529 b7d38981 Dimitris Aragiorgis
                client.ConnectNetwork(network.backend_id, group, network.mode,
530 2d762302 Dimitris Aragiorgis
                                      network.link, conflicts_check,
531 2d762302 Dimitris Aragiorgis
                                      depend_jobs)
532 22ee6892 Christos Stavrakakis
533 22ee6892 Christos Stavrakakis
534 3524241a Christos Stavrakakis
def delete_network(network, backends=None, disconnect=True):
535 22ee6892 Christos Stavrakakis
    if not backends:
536 22ee6892 Christos Stavrakakis
        backends = Backend.objects.exclude(offline=True)
537 22ee6892 Christos Stavrakakis
538 bf5c82dc Christos Stavrakakis
    log.debug("Deleting network %s from backends %s", network, backends)
539 bf5c82dc Christos Stavrakakis
540 22ee6892 Christos Stavrakakis
    for backend in backends:
541 3524241a Christos Stavrakakis
        disconnect_jobIDs = []
542 3524241a Christos Stavrakakis
        if disconnect:
543 3524241a Christos Stavrakakis
            disconnect_jobIDs = disconnect_network(network, backend)
544 3524241a Christos Stavrakakis
        _delete_network(network, backend, disconnect_jobIDs)
545 22ee6892 Christos Stavrakakis
546 22ee6892 Christos Stavrakakis
547 3524241a Christos Stavrakakis
def _delete_network(network, backend, depend_jobs=[]):
548 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
549 3524241a Christos Stavrakakis
        return client.DeleteNetwork(network.backend_id, depend_jobs)
550 22ee6892 Christos Stavrakakis
551 22ee6892 Christos Stavrakakis
552 3524241a Christos Stavrakakis
def disconnect_network(network, backend, group=None):
553 bf5c82dc Christos Stavrakakis
    log.debug("Disconnecting network %s to backend %s", network, backend)
554 22ee6892 Christos Stavrakakis
555 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
556 3524241a Christos Stavrakakis
        if group:
557 3524241a Christos Stavrakakis
            return [client.DisconnectNetwork(network.backend_id, group)]
558 3524241a Christos Stavrakakis
        else:
559 3524241a Christos Stavrakakis
            jobs = []
560 3524241a Christos Stavrakakis
            for group in client.GetGroups():
561 3524241a Christos Stavrakakis
                job = client.DisconnectNetwork(network.backend_id, group)
562 3524241a Christos Stavrakakis
                jobs.append(job)
563 3524241a Christos Stavrakakis
            return jobs
564 36f4cb29 Christos Stavrakakis
565 36f4cb29 Christos Stavrakakis
566 87920bc3 Christos Stavrakakis
def connect_to_network(vm, network, address=None):
567 3524241a Christos Stavrakakis
    nic = {'ip': address, 'network': network.backend_id}
568 22ee6892 Christos Stavrakakis
569 bf5c82dc Christos Stavrakakis
    log.debug("Connecting vm %s to network %s(%s)", vm, network, address)
570 22ee6892 Christos Stavrakakis
571 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
572 3524241a Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, nics=[('add',  nic)],
573 3524241a Christos Stavrakakis
                                     hotplug=settings.GANETI_USE_HOTPLUG,
574 3524241a Christos Stavrakakis
                                     dry_run=settings.TEST)
575 22ee6892 Christos Stavrakakis
576 22ee6892 Christos Stavrakakis
577 3524241a Christos Stavrakakis
def disconnect_from_network(vm, nic):
578 3524241a Christos Stavrakakis
    op = [('remove', nic.index, {})]
579 22ee6892 Christos Stavrakakis
580 bf5c82dc Christos Stavrakakis
    log.debug("Removing nic of VM %s, with index %s", vm, str(nic.index))
581 22ee6892 Christos Stavrakakis
582 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
583 3524241a Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, nics=op,
584 3524241a Christos Stavrakakis
                                     hotplug=settings.GANETI_USE_HOTPLUG,
585 3524241a Christos Stavrakakis
                                     dry_run=settings.TEST)
586 91826390 Giorgos Verigakis
587 c25cc9ec Vangelis Koukis
588 91826390 Giorgos Verigakis
def set_firewall_profile(vm, profile):
589 26563957 Giorgos Verigakis
    try:
590 26563957 Giorgos Verigakis
        tag = _firewall_tags[profile]
591 26563957 Giorgos Verigakis
    except KeyError:
592 91826390 Giorgos Verigakis
        raise ValueError("Unsopported Firewall Profile: %s" % profile)
593 37ca953f Christodoulos Psaltis
594 bf5c82dc Christos Stavrakakis
    log.debug("Setting tag of VM %s to %s", vm, profile)
595 bf5c82dc Christos Stavrakakis
596 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
597 3524241a Christos Stavrakakis
        # Delete all firewall tags
598 3524241a Christos Stavrakakis
        for t in _firewall_tags.values():
599 3524241a Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, [t],
600 3524241a Christos Stavrakakis
                                      dry_run=settings.TEST)
601 37ca953f Christodoulos Psaltis
602 3524241a Christos Stavrakakis
        client.AddInstanceTags(vm.backend_vm_id, [tag], dry_run=settings.TEST)
603 9afeb669 Kostas Papadimitriou
604 3524241a Christos Stavrakakis
        # XXX NOP ModifyInstance call to force process_net_status to run
605 3524241a Christos Stavrakakis
        # on the dispatcher
606 cc92b70f Christos Stavrakakis
        os_name = settings.GANETI_CREATEINSTANCE_KWARGS['os']
607 3524241a Christos Stavrakakis
        client.ModifyInstance(vm.backend_vm_id,
608 cc92b70f Christos Stavrakakis
                              os_name=os_name)
609 5eedb0e4 Vangelis Koukis
610 41303ed0 Vangelis Koukis
611 f5b4f2a3 Christos Stavrakakis
def get_ganeti_instances(backend=None, bulk=False):
612 3524241a Christos Stavrakakis
    instances = []
613 3524241a Christos Stavrakakis
    for backend in get_backends(backend):
614 3524241a Christos Stavrakakis
        with pooled_rapi_client(backend) as client:
615 3524241a Christos Stavrakakis
            instances.append(client.GetInstances(bulk=bulk))
616 3524241a Christos Stavrakakis
617 3524241a Christos Stavrakakis
    return reduce(list.__add__, instances, [])
618 f5b4f2a3 Christos Stavrakakis
619 f5b4f2a3 Christos Stavrakakis
620 f5b4f2a3 Christos Stavrakakis
def get_ganeti_nodes(backend=None, bulk=False):
621 3524241a Christos Stavrakakis
    nodes = []
622 3524241a Christos Stavrakakis
    for backend in get_backends(backend):
623 3524241a Christos Stavrakakis
        with pooled_rapi_client(backend) as client:
624 3524241a Christos Stavrakakis
            nodes.append(client.GetNodes(bulk=bulk))
625 3524241a Christos Stavrakakis
626 3524241a Christos Stavrakakis
    return reduce(list.__add__, nodes, [])
627 f5b4f2a3 Christos Stavrakakis
628 f5b4f2a3 Christos Stavrakakis
629 f5b4f2a3 Christos Stavrakakis
def get_ganeti_jobs(backend=None, bulk=False):
630 3524241a Christos Stavrakakis
    jobs = []
631 3524241a Christos Stavrakakis
    for backend in get_backends(backend):
632 3524241a Christos Stavrakakis
        with pooled_rapi_client(backend) as client:
633 3524241a Christos Stavrakakis
            jobs.append(client.GetJobs(bulk=bulk))
634 3524241a Christos Stavrakakis
    return reduce(list.__add__, jobs, [])
635 f5b4f2a3 Christos Stavrakakis
636 f5b4f2a3 Christos Stavrakakis
##
637 f5b4f2a3 Christos Stavrakakis
##
638 f5b4f2a3 Christos Stavrakakis
##
639 1a894bfe Christos Stavrakakis
640 1a894bfe Christos Stavrakakis
641 f5b4f2a3 Christos Stavrakakis
def get_backends(backend=None):
642 f5b4f2a3 Christos Stavrakakis
    if backend:
643 c414bc87 Christos Stavrakakis
        if backend.offline:
644 c414bc87 Christos Stavrakakis
            return []
645 f5b4f2a3 Christos Stavrakakis
        return [backend]
646 cc7c0f44 Christos Stavrakakis
    return Backend.objects.filter(offline=False)
647 f5b4f2a3 Christos Stavrakakis
648 17852fe9 Giorgos Verigakis
649 1a894bfe Christos Stavrakakis
def get_physical_resources(backend):
650 1a894bfe Christos Stavrakakis
    """ Get the physical resources of a backend.
651 1a894bfe Christos Stavrakakis

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

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

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

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

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