Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (26.6 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 529178b1 Giorgos Verigakis
from django.conf import settings
34 207b70d5 Giorgos Verigakis
from django.db import transaction
35 1a894bfe Christos Stavrakakis
from datetime import datetime
36 207b70d5 Giorgos Verigakis
37 22ee6892 Christos Stavrakakis
from synnefo.db.models import (Backend, VirtualMachine, Network,
38 3524241a Christos Stavrakakis
                               BackendNetwork, BACKEND_STATUSES,
39 fd95834e Christos Stavrakakis
                               pooled_rapi_client, VirtualMachineDiagnostic)
40 03992c72 Christos Stavrakakis
from synnefo.logic import utils
41 cb4eee84 Christos Stavrakakis
from synnefo import quotas
42 b7d38981 Dimitris Aragiorgis
from synnefo.api.util import release_resource
43 fd95834e Christos Stavrakakis
from synnefo.util.mac2eui64 import mac2eui64
44 529178b1 Giorgos Verigakis
45 3524241a Christos Stavrakakis
from logging import getLogger
46 3524241a Christos Stavrakakis
log = getLogger(__name__)
47 9e98ba3c Giorgos Verigakis
48 529178b1 Giorgos Verigakis
49 efff6193 Giorgos Verigakis
_firewall_tags = {
50 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
51 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
52 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
53 efff6193 Giorgos Verigakis
54 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
55 efff6193 Giorgos Verigakis
56 02feca11 Vassilios Karakoidas
57 093f9c53 Vangelis Koukis
@transaction.commit_on_success
58 fd95834e Christos Stavrakakis
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None):
59 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
60 02feca11 Vassilios Karakoidas

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

65 02feca11 Vassilios Karakoidas
    """
66 41303ed0 Vangelis Koukis
    # See #1492, #1031, #1111 why this line has been removed
67 41303ed0 Vangelis Koukis
    #if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
68 fd65ab41 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
69 02feca11 Vassilios Karakoidas
        raise VirtualMachine.InvalidBackendMsgError(opcode, status)
70 02feca11 Vassilios Karakoidas
71 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = jobid
72 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = status
73 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = opcode
74 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = logmsg
75 02feca11 Vassilios Karakoidas
76 02feca11 Vassilios Karakoidas
    # Notifications of success change the operating state
77 41303ed0 Vangelis Koukis
    state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode, None)
78 41303ed0 Vangelis Koukis
    if status == 'success' and state_for_success is not None:
79 c4ce868e Christos Stavrakakis
        vm.operstate = state_for_success
80 685b219e Vangelis Koukis
81 fd95834e Christos Stavrakakis
    # Update the NICs of the VM
82 fd95834e Christos Stavrakakis
    if status == "success" and nics is not None:
83 fd95834e Christos Stavrakakis
        _process_net_status(vm, etime, nics)
84 fd95834e Christos Stavrakakis
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 07602322 Christos Stavrakakis
        if (status == 'success' or
96 07602322 Christos Stavrakakis
           (status == 'error' and (vm.operstate == 'ERROR' or
97 07602322 Christos Stavrakakis
                                   vm.action == 'DESTROY'))):
98 40d53b77 Christos Stavrakakis
            _process_net_status(vm, etime, nics=[])
99 e97288bc Christos Stavrakakis
            vm.deleted = True
100 e97288bc Christos Stavrakakis
            vm.operstate = state_for_success
101 e97288bc Christos Stavrakakis
            vm.backendtime = etime
102 2509ce17 Christos Stavrakakis
            # Issue and accept commission to Quotaholder
103 2509ce17 Christos Stavrakakis
            quotas.issue_and_accept_commission(vm, delete=True)
104 093f9c53 Vangelis Koukis
105 c4ce868e Christos Stavrakakis
    # Update backendtime only for jobs that have been successfully completed,
106 c4ce868e Christos Stavrakakis
    # since only these jobs update the state of the VM. Else a "race condition"
107 c4ce868e Christos Stavrakakis
    # may occur when a successful job (e.g. OP_INSTANCE_REMOVE) completes
108 c4ce868e Christos Stavrakakis
    # before an error job and messages arrive in reversed order.
109 c4ce868e Christos Stavrakakis
    if status == 'success':
110 c4ce868e Christos Stavrakakis
        vm.backendtime = etime
111 02feca11 Vassilios Karakoidas
112 02feca11 Vassilios Karakoidas
    vm.save()
113 22e52ede Vassilios Karakoidas
114 ad2d6807 Vangelis Koukis
115 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
116 c4e55622 Christos Stavrakakis
def process_net_status(vm, etime, nics):
117 fd95834e Christos Stavrakakis
    """Wrap _process_net_status inside transaction."""
118 fd95834e Christos Stavrakakis
    _process_net_status(vm, etime, nics)
119 fd95834e Christos Stavrakakis
120 fd95834e Christos Stavrakakis
121 fd95834e 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 40d53b77 Christos Stavrakakis
        return
134 40d53b77 Christos Stavrakakis
135 40d53b77 Christos Stavrakakis
    # Get X-Lock on backend before getting X-Lock on network IP pools, to
136 40d53b77 Christos Stavrakakis
    # guarantee that no deadlock will occur with Backend allocator.
137 40d53b77 Christos Stavrakakis
    Backend.objects.select_for_update().get(id=vm.backend_id)
138 b578d9e7 Christos Stavrakakis
139 fdc94944 Christos Stavrakakis
    release_instance_nics(vm)
140 77f0fa63 Christos Stavrakakis
141 b578d9e7 Christos Stavrakakis
    for nic in ganeti_nics:
142 b578d9e7 Christos Stavrakakis
        ipv4 = nic.get('ipv4', '')
143 f45a7ac4 Christos Stavrakakis
        net = nic['network']
144 b578d9e7 Christos Stavrakakis
        if ipv4:
145 b578d9e7 Christos Stavrakakis
            net.reserve_address(ipv4)
146 b578d9e7 Christos Stavrakakis
147 b578d9e7 Christos Stavrakakis
        nic['dirty'] = False
148 b578d9e7 Christos Stavrakakis
        vm.nics.create(**nic)
149 b578d9e7 Christos Stavrakakis
        # Dummy save the network, because UI uses changed-since for VMs
150 b578d9e7 Christos Stavrakakis
        # and Networks in order to show the VM NICs
151 b578d9e7 Christos Stavrakakis
        net.save()
152 b578d9e7 Christos Stavrakakis
153 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
154 b578d9e7 Christos Stavrakakis
    vm.save()
155 b578d9e7 Christos Stavrakakis
156 b578d9e7 Christos Stavrakakis
157 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
158 b578d9e7 Christos Stavrakakis
    """Process NIC dict from ganeti hooks."""
159 b578d9e7 Christos Stavrakakis
    new_nics = []
160 b578d9e7 Christos Stavrakakis
    for i, new_nic in enumerate(ganeti_nics):
161 77f0fa63 Christos Stavrakakis
        network = new_nic.get('network', '')
162 22ee6892 Christos Stavrakakis
        n = str(network)
163 77f0fa63 Christos Stavrakakis
        pk = utils.id_from_network_name(n)
164 77f0fa63 Christos Stavrakakis
165 fdc94944 Christos Stavrakakis
        net = Network.objects.get(pk=pk)
166 77f0fa63 Christos Stavrakakis
167 77f0fa63 Christos Stavrakakis
        # Get the new nic info
168 77f0fa63 Christos Stavrakakis
        mac = new_nic.get('mac', '')
169 77f0fa63 Christos Stavrakakis
        ipv4 = new_nic.get('ip', '')
170 fd95834e Christos Stavrakakis
        if net.subnet6:
171 fd95834e Christos Stavrakakis
            ipv6 = mac2eui64(mac, net.subnet6)
172 fd95834e Christos Stavrakakis
        else:
173 fd95834e Christos Stavrakakis
            ipv6 = ''
174 9afeb669 Kostas Papadimitriou
175 77f0fa63 Christos Stavrakakis
        firewall = new_nic.get('firewall', '')
176 658a825a Giorgos Verigakis
        firewall_profile = _reverse_tags.get(firewall, '')
177 658a825a Giorgos Verigakis
        if not firewall_profile and net.public:
178 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
179 9afeb669 Kostas Papadimitriou
180 b578d9e7 Christos Stavrakakis
        nic = {
181 cc92b70f Christos Stavrakakis
            'index': i,
182 cc92b70f Christos Stavrakakis
            'network': net,
183 cc92b70f Christos Stavrakakis
            'mac': mac,
184 cc92b70f Christos Stavrakakis
            'ipv4': ipv4,
185 cc92b70f Christos Stavrakakis
            'ipv6': ipv6,
186 939d71dd Christos Stavrakakis
            'firewall_profile': firewall_profile,
187 939d71dd Christos Stavrakakis
            'state': 'ACTIVE'}
188 b578d9e7 Christos Stavrakakis
189 b578d9e7 Christos Stavrakakis
        new_nics.append(nic)
190 b578d9e7 Christos Stavrakakis
    return new_nics
191 b578d9e7 Christos Stavrakakis
192 b578d9e7 Christos Stavrakakis
193 b578d9e7 Christos Stavrakakis
def nics_changed(old_nics, new_nics):
194 b578d9e7 Christos Stavrakakis
    """Return True if NICs have changed in any way."""
195 b578d9e7 Christos Stavrakakis
    if len(old_nics) != len(new_nics):
196 b578d9e7 Christos Stavrakakis
        return True
197 be4d8aed Christos Stavrakakis
    fields = ["ipv4", "ipv6", "mac", "firewall_profile", "index", "network"]
198 b578d9e7 Christos Stavrakakis
    for old_nic, new_nic in zip(old_nics, new_nics):
199 be4d8aed Christos Stavrakakis
        for field in fields:
200 be4d8aed Christos Stavrakakis
            if getattr(old_nic, field) != new_nic[field]:
201 be4d8aed Christos Stavrakakis
                return True
202 b578d9e7 Christos Stavrakakis
    return False
203 22ee6892 Christos Stavrakakis
204 22ee6892 Christos Stavrakakis
205 bd392934 Christos Stavrakakis
def release_instance_nics(vm):
206 bd392934 Christos Stavrakakis
    for nic in vm.nics.all():
207 40ef487d Christos Stavrakakis
        net = nic.network
208 fa454545 Christos Stavrakakis
        if nic.ipv4:
209 40ef487d Christos Stavrakakis
            net.release_address(nic.ipv4)
210 bd392934 Christos Stavrakakis
        nic.delete()
211 40ef487d Christos Stavrakakis
        net.save()
212 bd392934 Christos Stavrakakis
213 bd392934 Christos Stavrakakis
214 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
215 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
216 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
217 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
218 22ee6892 Christos Stavrakakis
219 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
220 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
221 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
222 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
223 22ee6892 Christos Stavrakakis
224 05146623 Christos Stavrakakis
    network = back_network.network
225 05146623 Christos Stavrakakis
226 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
227 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
228 22ee6892 Christos Stavrakakis
    if status == 'success' and state_for_success is not None:
229 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
230 22ee6892 Christos Stavrakakis
231 e6f6627c Christos Stavrakakis
    if status in ('canceled', 'error') and opcode == 'OP_NETWORK_ADD':
232 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
233 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
234 22ee6892 Christos Stavrakakis
235 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
236 05146623 Christos Stavrakakis
        if (status == 'success' or
237 05146623 Christos Stavrakakis
           (status == 'error' and (back_network.operstate == 'ERROR' or
238 05146623 Christos Stavrakakis
                                   network.action == 'DESTROY'))):
239 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
240 e97288bc Christos Stavrakakis
            back_network.deleted = True
241 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
242 22ee6892 Christos Stavrakakis
243 e97288bc Christos Stavrakakis
    if status == 'success':
244 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
245 22ee6892 Christos Stavrakakis
    back_network.save()
246 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
247 05146623 Christos Stavrakakis
    update_network_state(network)
248 fd2bdbb2 Christos Stavrakakis
249 fd2bdbb2 Christos Stavrakakis
250 2509ce17 Christos Stavrakakis
def update_network_state(network):
251 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
252 cb4eee84 Christos Stavrakakis

253 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
254 99af08a4 Christos Stavrakakis
    backends that network exists.
255 99af08a4 Christos Stavrakakis

256 99af08a4 Christos Stavrakakis
    The state of the network is:
257 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
258 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
259 99af08a4 Christos Stavrakakis

260 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
261 99af08a4 Christos Stavrakakis
    quotas for the network.
262 99af08a4 Christos Stavrakakis

263 99af08a4 Christos Stavrakakis
    """
264 99af08a4 Christos Stavrakakis
    if network.deleted:
265 99af08a4 Christos Stavrakakis
        # Network has already been deleted. Just assert that state is also
266 99af08a4 Christos Stavrakakis
        # DELETED
267 99af08a4 Christos Stavrakakis
        if not network.state == "DELETED":
268 99af08a4 Christos Stavrakakis
            network.state = "DELETED"
269 99af08a4 Christos Stavrakakis
            network.save()
270 cb4eee84 Christos Stavrakakis
        return
271 cb4eee84 Christos Stavrakakis
272 99af08a4 Christos Stavrakakis
    backend_states = [s.operstate for s in network.backend_networks.all()]
273 27cda06b Christos Stavrakakis
    if not backend_states and network.action != "DESTROY":
274 99af08a4 Christos Stavrakakis
        if network.state != "ACTIVE":
275 99af08a4 Christos Stavrakakis
            network.state = "ACTIVE"
276 99af08a4 Christos Stavrakakis
            network.save()
277 99af08a4 Christos Stavrakakis
            return
278 99af08a4 Christos Stavrakakis
279 99af08a4 Christos Stavrakakis
    # Network is deleted when all BackendNetworks go to "DELETED" operstate
280 27cda06b Christos Stavrakakis
    deleted = reduce(lambda x, y: x == y and "DELETED", backend_states,
281 27cda06b Christos Stavrakakis
                     "DELETED")
282 99af08a4 Christos Stavrakakis
283 cb4eee84 Christos Stavrakakis
    # Release the resources on the deletion of the Network
284 99af08a4 Christos Stavrakakis
    if deleted:
285 cb4eee84 Christos Stavrakakis
        log.info("Network %r deleted. Releasing link %r mac_prefix %r",
286 cb4eee84 Christos Stavrakakis
                 network.id, network.mac_prefix, network.link)
287 cb4eee84 Christos Stavrakakis
        network.deleted = True
288 99af08a4 Christos Stavrakakis
        network.state = "DELETED"
289 b7d38981 Dimitris Aragiorgis
        if network.mac_prefix:
290 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["mac_prefix"] == "pool":
291 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="mac_prefix",
292 b7d38981 Dimitris Aragiorgis
                                 value=network.mac_prefix)
293 b7d38981 Dimitris Aragiorgis
        if network.link:
294 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["link"] == "pool":
295 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="bridge", value=network.link)
296 cb4eee84 Christos Stavrakakis
297 cb4eee84 Christos Stavrakakis
        # Issue commission
298 e18c1749 Christos Stavrakakis
        if network.userid:
299 2509ce17 Christos Stavrakakis
            quotas.issue_and_accept_commission(network, delete=True)
300 e18c1749 Christos Stavrakakis
        elif not network.public:
301 e18c1749 Christos Stavrakakis
            log.warning("Network %s does not have an owner!", network.id)
302 cb4eee84 Christos Stavrakakis
    network.save()
303 cb4eee84 Christos Stavrakakis
304 cb4eee84 Christos Stavrakakis
305 fd2bdbb2 Christos Stavrakakis
@transaction.commit_on_success
306 fd2bdbb2 Christos Stavrakakis
def process_network_modify(back_network, etime, jobid, opcode, status,
307 fd2bdbb2 Christos Stavrakakis
                           add_reserved_ips, remove_reserved_ips):
308 fd2bdbb2 Christos Stavrakakis
    assert (opcode == "OP_NETWORK_SET_PARAMS")
309 fd2bdbb2 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
310 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
311 fd2bdbb2 Christos Stavrakakis
312 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobid = jobid
313 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobstatus = status
314 fd2bdbb2 Christos Stavrakakis
    back_network.opcode = opcode
315 fd2bdbb2 Christos Stavrakakis
316 fd2bdbb2 Christos Stavrakakis
    if add_reserved_ips or remove_reserved_ips:
317 fd2bdbb2 Christos Stavrakakis
        net = back_network.network
318 fd2bdbb2 Christos Stavrakakis
        pool = net.get_pool()
319 fd2bdbb2 Christos Stavrakakis
        if add_reserved_ips:
320 fd2bdbb2 Christos Stavrakakis
            for ip in add_reserved_ips:
321 fd2bdbb2 Christos Stavrakakis
                pool.reserve(ip, external=True)
322 fd2bdbb2 Christos Stavrakakis
        if remove_reserved_ips:
323 fd2bdbb2 Christos Stavrakakis
            for ip in remove_reserved_ips:
324 fd2bdbb2 Christos Stavrakakis
                pool.put(ip, external=True)
325 fd2bdbb2 Christos Stavrakakis
        pool.save()
326 fd2bdbb2 Christos Stavrakakis
327 fd2bdbb2 Christos Stavrakakis
    if status == 'success':
328 fd2bdbb2 Christos Stavrakakis
        back_network.backendtime = etime
329 fd2bdbb2 Christos Stavrakakis
    back_network.save()
330 ad2d6807 Vangelis Koukis
331 c25cc9ec Vangelis Koukis
332 9068cd85 Georgios Gousios
@transaction.commit_on_success
333 0827883e Nikos Skalkotos
def process_create_progress(vm, etime, progress):
334 9068cd85 Georgios Gousios
335 0827883e Nikos Skalkotos
    percentage = int(progress)
336 9068cd85 Georgios Gousios
337 af90d919 Vangelis Koukis
    # The percentage may exceed 100%, due to the way
338 0827883e Nikos Skalkotos
    # snf-image:copy-progress tracks bytes read by image handling processes
339 af90d919 Vangelis Koukis
    percentage = 100 if percentage > 100 else percentage
340 af90d919 Vangelis Koukis
    if percentage < 0:
341 af90d919 Vangelis Koukis
        raise ValueError("Percentage cannot be negative")
342 9068cd85 Georgios Gousios
343 7ec9558b Vangelis Koukis
    # FIXME: log a warning here, see #1033
344 7ec9558b Vangelis Koukis
#   if last_update > percentage:
345 7ec9558b Vangelis Koukis
#       raise ValueError("Build percentage should increase monotonically " \
346 7ec9558b Vangelis Koukis
#                        "(old = %d, new = %d)" % (last_update, percentage))
347 9068cd85 Georgios Gousios
348 c25cc9ec Vangelis Koukis
    # This assumes that no message of type 'ganeti-create-progress' is going to
349 c25cc9ec Vangelis Koukis
    # arrive once OP_INSTANCE_CREATE has succeeded for a Ganeti instance and
350 c25cc9ec Vangelis Koukis
    # the instance is STARTED.  What if the two messages are processed by two
351 c25cc9ec Vangelis Koukis
    # separate dispatcher threads, and the 'ganeti-op-status' message for
352 c25cc9ec Vangelis Koukis
    # successful creation gets processed before the 'ganeti-create-progress'
353 c25cc9ec Vangelis Koukis
    # message? [vkoukis]
354 c25cc9ec Vangelis Koukis
    #
355 c25cc9ec Vangelis Koukis
    #if not vm.operstate == 'BUILD':
356 c25cc9ec Vangelis Koukis
    #    raise VirtualMachine.IllegalState("VM is not in building state")
357 9068cd85 Georgios Gousios
358 c25cc9ec Vangelis Koukis
    vm.buildpercentage = percentage
359 c4e55622 Christos Stavrakakis
    vm.backendtime = etime
360 9068cd85 Georgios Gousios
    vm.save()
361 ad2d6807 Vangelis Koukis
362 c25cc9ec Vangelis Koukis
363 952b2a48 Christos Stavrakakis
@transaction.commit_on_success
364 7d43565f Kostas Papadimitriou
def create_instance_diagnostic(vm, message, source, level="DEBUG", etime=None,
365 cc92b70f Christos Stavrakakis
                               details=None):
366 7d43565f Kostas Papadimitriou
    """
367 7d43565f Kostas Papadimitriou
    Create virtual machine instance diagnostic entry.
368 7d43565f Kostas Papadimitriou

369 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
370 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
371 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
372 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
373 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
374 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
375 7d43565f Kostas Papadimitriou
    """
376 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
377 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
378 cc92b70f Christos Stavrakakis
                                                   message=message,
379 cc92b70f Christos Stavrakakis
                                                   details=details)
380 7d43565f Kostas Papadimitriou
381 7d43565f Kostas Papadimitriou
382 8528b8ac Christos Stavrakakis
def create_instance(vm, public_nic, flavor, image):
383 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
384 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
385 7f691719 Christos Stavrakakis

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

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

689 1a894bfe Christos Stavrakakis
    """
690 1a894bfe Christos Stavrakakis
    nodes = get_ganeti_nodes(backend, bulk=True)
691 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
692 1a894bfe Christos Stavrakakis
    res = {}
693 1a894bfe Christos Stavrakakis
    for a in attr:
694 1a894bfe Christos Stavrakakis
        res[a] = 0
695 1a894bfe Christos Stavrakakis
    for n in nodes:
696 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
697 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
698 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
699 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
700 1a894bfe Christos Stavrakakis
            for a in attr:
701 1a894bfe Christos Stavrakakis
                res[a] += int(n[a])
702 1a894bfe Christos Stavrakakis
    return res
703 1a894bfe Christos Stavrakakis
704 1a894bfe Christos Stavrakakis
705 1a894bfe Christos Stavrakakis
def update_resources(backend, resources=None):
706 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
707 1a894bfe Christos Stavrakakis

708 1a894bfe Christos Stavrakakis
    """
709 17852fe9 Giorgos Verigakis
710 1a894bfe Christos Stavrakakis
    if not resources:
711 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
712 41303ed0 Vangelis Koukis
713 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
714 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
715 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
716 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
717 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
718 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
719 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
720 1a894bfe Christos Stavrakakis
    backend.save()
721 1a894bfe Christos Stavrakakis
722 1a894bfe Christos Stavrakakis
723 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
724 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
725 1a894bfe Christos Stavrakakis

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

729 1a894bfe Christos Stavrakakis
    """
730 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
731 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
732 1a894bfe Christos Stavrakakis
    mem = 0
733 1a894bfe Christos Stavrakakis
    for i in instances:
734 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
735 1a894bfe Christos Stavrakakis
    return mem
736 b3d28af2 Christos Stavrakakis
737 b3d28af2 Christos Stavrakakis
##
738 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
739 b3d28af2 Christos Stavrakakis
##
740 b3d28af2 Christos Stavrakakis
741 b3d28af2 Christos Stavrakakis
742 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
743 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
744 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
745 b3d28af2 Christos Stavrakakis
        return result
746 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
747 b3d28af2 Christos Stavrakakis
    return result
748 b3d28af2 Christos Stavrakakis
749 b3d28af2 Christos Stavrakakis
750 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
751 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
752 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
753 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
754 3524241a Christos Stavrakakis
    return result
755 b3d28af2 Christos Stavrakakis
756 b3d28af2 Christos Stavrakakis
757 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
758 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
759 3524241a Christos Stavrakakis
        for group in client.GetGroups():
760 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
761 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
762 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
763 3524241a Christos Stavrakakis
            if result[0] != 'success':
764 3524241a Christos Stavrakakis
                return result
765 b3d28af2 Christos Stavrakakis
766 b3d28af2 Christos Stavrakakis
    return result
767 b3d28af2 Christos Stavrakakis
768 b3d28af2 Christos Stavrakakis
769 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
770 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
771 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
772 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
773 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
774 cc92b70f Christos Stavrakakis
                                         [result], None)
775 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
776 b3d28af2 Christos Stavrakakis
777 b3d28af2 Christos Stavrakakis
    if status == 'success':
778 b3d28af2 Christos Stavrakakis
        return (status, None)
779 b3d28af2 Christos Stavrakakis
    else:
780 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
781 b3d28af2 Christos Stavrakakis
        return (status, error)