Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / backend.py @ 6ec4694f

History | View | Annotate | Download (25.7 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 529178b1 Giorgos Verigakis
47 3524241a Christos Stavrakakis
from logging import getLogger
48 3524241a Christos Stavrakakis
log = getLogger(__name__)
49 9e98ba3c Giorgos Verigakis
50 529178b1 Giorgos Verigakis
51 efff6193 Giorgos Verigakis
_firewall_tags = {
52 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
53 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
54 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
55 efff6193 Giorgos Verigakis
56 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
57 efff6193 Giorgos Verigakis
58 02feca11 Vassilios Karakoidas
59 cb4eee84 Christos Stavrakakis
@quotas.uses_commission
60 093f9c53 Vangelis Koukis
@transaction.commit_on_success
61 cb4eee84 Christos Stavrakakis
def process_op_status(serials, vm, etime, jobid, opcode, status, logmsg):
62 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
63 02feca11 Vassilios Karakoidas

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

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

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

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

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

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

670 1a894bfe Christos Stavrakakis
    """
671 1a894bfe Christos Stavrakakis
    nodes = get_ganeti_nodes(backend, bulk=True)
672 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
673 1a894bfe Christos Stavrakakis
    res = {}
674 1a894bfe Christos Stavrakakis
    for a in attr:
675 1a894bfe Christos Stavrakakis
        res[a] = 0
676 1a894bfe Christos Stavrakakis
    for n in nodes:
677 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
678 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
679 1a894bfe Christos Stavrakakis
        if n['vm_capable'] and not n['drained'] and not n['offline']\
680 1a894bfe Christos Stavrakakis
           and n['cnodes']:
681 1a894bfe Christos Stavrakakis
            for a in attr:
682 1a894bfe Christos Stavrakakis
                res[a] += int(n[a])
683 1a894bfe Christos Stavrakakis
    return res
684 1a894bfe Christos Stavrakakis
685 1a894bfe Christos Stavrakakis
686 1a894bfe Christos Stavrakakis
def update_resources(backend, resources=None):
687 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
688 1a894bfe Christos Stavrakakis

689 1a894bfe Christos Stavrakakis
    """
690 17852fe9 Giorgos Verigakis
691 1a894bfe Christos Stavrakakis
    if not resources:
692 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
693 41303ed0 Vangelis Koukis
694 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
695 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
696 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
697 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
698 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
699 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
700 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
701 1a894bfe Christos Stavrakakis
    backend.save()
702 1a894bfe Christos Stavrakakis
703 1a894bfe Christos Stavrakakis
704 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
705 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
706 1a894bfe Christos Stavrakakis

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

710 1a894bfe Christos Stavrakakis
    """
711 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
712 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
713 1a894bfe Christos Stavrakakis
    mem = 0
714 1a894bfe Christos Stavrakakis
    for i in instances:
715 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
716 1a894bfe Christos Stavrakakis
    return mem
717 b3d28af2 Christos Stavrakakis
718 b3d28af2 Christos Stavrakakis
##
719 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
720 b3d28af2 Christos Stavrakakis
##
721 b3d28af2 Christos Stavrakakis
722 b3d28af2 Christos Stavrakakis
723 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
724 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
725 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
726 b3d28af2 Christos Stavrakakis
        return result
727 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
728 b3d28af2 Christos Stavrakakis
    return result
729 b3d28af2 Christos Stavrakakis
730 b3d28af2 Christos Stavrakakis
731 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
732 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
733 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
734 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
735 3524241a Christos Stavrakakis
    return result
736 b3d28af2 Christos Stavrakakis
737 b3d28af2 Christos Stavrakakis
738 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
739 839e2bd0 Christos Stavrakakis
    if network.type in ('PUBLIC_ROUTED', 'CUSTOM_ROUTED'):
740 839e2bd0 Christos Stavrakakis
        mode = 'routed'
741 839e2bd0 Christos Stavrakakis
    else:
742 839e2bd0 Christos Stavrakakis
        mode = 'bridged'
743 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
744 3524241a Christos Stavrakakis
        for group in client.GetGroups():
745 3524241a Christos Stavrakakis
            job = client.ConnectNetwork(network.backend_id, group, mode,
746 3524241a Christos Stavrakakis
                                        network.link)
747 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
748 3524241a Christos Stavrakakis
            if result[0] != 'success':
749 3524241a Christos Stavrakakis
                return result
750 b3d28af2 Christos Stavrakakis
751 b3d28af2 Christos Stavrakakis
    return result
752 b3d28af2 Christos Stavrakakis
753 b3d28af2 Christos Stavrakakis
754 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
755 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
756 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
757 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
758 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
759 b3d28af2 Christos Stavrakakis
                                        [result], None)
760 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
761 b3d28af2 Christos Stavrakakis
762 b3d28af2 Christos Stavrakakis
    if status == 'success':
763 b3d28af2 Christos Stavrakakis
        return (status, None)
764 b3d28af2 Christos Stavrakakis
    else:
765 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
766 b3d28af2 Christos Stavrakakis
        return (status, error)