Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (26.5 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 fd95834e Christos Stavrakakis
                               pooled_rapi_client, VirtualMachineDiagnostic)
43 03992c72 Christos Stavrakakis
from synnefo.logic import utils
44 cb4eee84 Christos Stavrakakis
from synnefo import quotas
45 b7d38981 Dimitris Aragiorgis
from synnefo.api.util import release_resource
46 fd95834e Christos Stavrakakis
from synnefo.util.mac2eui64 import mac2eui64
47 529178b1 Giorgos Verigakis
48 3524241a Christos Stavrakakis
from logging import getLogger
49 3524241a Christos Stavrakakis
log = getLogger(__name__)
50 9e98ba3c Giorgos Verigakis
51 529178b1 Giorgos Verigakis
52 efff6193 Giorgos Verigakis
_firewall_tags = {
53 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
54 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
55 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
56 efff6193 Giorgos Verigakis
57 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
58 efff6193 Giorgos Verigakis
59 02feca11 Vassilios Karakoidas
60 093f9c53 Vangelis Koukis
@transaction.commit_on_success
61 fd95834e Christos Stavrakakis
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None):
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 fd95834e Christos Stavrakakis
    # Update the NICs of the VM
85 fd95834e Christos Stavrakakis
    if status == "success" and nics is not None:
86 fd95834e Christos Stavrakakis
        _process_net_status(vm, etime, nics)
87 fd95834e Christos Stavrakakis
88 91954b45 Christos Stavrakakis
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
89 cb4eee84 Christos Stavrakakis
    if opcode == 'OP_INSTANCE_CREATE' and status in ('canceled', 'error'):
90 c4ce868e Christos Stavrakakis
        vm.operstate = 'ERROR'
91 c4ce868e Christos Stavrakakis
        vm.backendtime = etime
92 cb4eee84 Christos Stavrakakis
    elif opcode == 'OP_INSTANCE_REMOVE':
93 e97288bc Christos Stavrakakis
        # Set the deleted flag explicitly, cater for admin-initiated removals
94 e97288bc Christos Stavrakakis
        # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR,
95 e97288bc Christos Stavrakakis
        # when no instance exists at the Ganeti backend.
96 e97288bc Christos Stavrakakis
        # See ticket #799 for all the details.
97 e97288bc Christos Stavrakakis
        #
98 e97288bc Christos Stavrakakis
        if status == 'success' or (status == 'error' and
99 e97288bc Christos Stavrakakis
                                   vm.operstate == 'ERROR'):
100 40d53b77 Christos Stavrakakis
            _process_net_status(vm, etime, nics=[])
101 e97288bc Christos Stavrakakis
            vm.deleted = True
102 e97288bc Christos Stavrakakis
            vm.operstate = state_for_success
103 e97288bc Christos Stavrakakis
            vm.backendtime = etime
104 2509ce17 Christos Stavrakakis
            # Issue and accept commission to Quotaholder
105 2509ce17 Christos Stavrakakis
            quotas.issue_and_accept_commission(vm, delete=True)
106 093f9c53 Vangelis Koukis
107 c4ce868e Christos Stavrakakis
    # Update backendtime only for jobs that have been successfully completed,
108 c4ce868e Christos Stavrakakis
    # since only these jobs update the state of the VM. Else a "race condition"
109 c4ce868e Christos Stavrakakis
    # may occur when a successful job (e.g. OP_INSTANCE_REMOVE) completes
110 c4ce868e Christos Stavrakakis
    # before an error job and messages arrive in reversed order.
111 c4ce868e Christos Stavrakakis
    if status == 'success':
112 c4ce868e Christos Stavrakakis
        vm.backendtime = etime
113 02feca11 Vassilios Karakoidas
114 02feca11 Vassilios Karakoidas
    vm.save()
115 22e52ede Vassilios Karakoidas
116 ad2d6807 Vangelis Koukis
117 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
118 c4e55622 Christos Stavrakakis
def process_net_status(vm, etime, nics):
119 fd95834e Christos Stavrakakis
    """Wrap _process_net_status inside transaction."""
120 fd95834e Christos Stavrakakis
    _process_net_status(vm, etime, nics)
121 fd95834e Christos Stavrakakis
122 fd95834e Christos Stavrakakis
123 fd95834e Christos Stavrakakis
def _process_net_status(vm, etime, nics):
124 ad2d6807 Vangelis Koukis
    """Process a net status notification from the backend
125 ad2d6807 Vangelis Koukis

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

129 ad2d6807 Vangelis Koukis
    Update the state of the VM in the DB accordingly.
130 ad2d6807 Vangelis Koukis
    """
131 37ca953f Christodoulos Psaltis
132 b578d9e7 Christos Stavrakakis
    ganeti_nics = process_ganeti_nics(nics)
133 b578d9e7 Christos Stavrakakis
    if not nics_changed(vm.nics.order_by('index'), ganeti_nics):
134 b578d9e7 Christos Stavrakakis
        log.debug("NICs for VM %s have not changed", vm)
135 40d53b77 Christos Stavrakakis
        return
136 40d53b77 Christos Stavrakakis
137 40d53b77 Christos Stavrakakis
    # Get X-Lock on backend before getting X-Lock on network IP pools, to
138 40d53b77 Christos Stavrakakis
    # guarantee that no deadlock will occur with Backend allocator.
139 40d53b77 Christos Stavrakakis
    Backend.objects.select_for_update().get(id=vm.backend_id)
140 b578d9e7 Christos Stavrakakis
141 fdc94944 Christos Stavrakakis
    release_instance_nics(vm)
142 77f0fa63 Christos Stavrakakis
143 b578d9e7 Christos Stavrakakis
    for nic in ganeti_nics:
144 b578d9e7 Christos Stavrakakis
        ipv4 = nic.get('ipv4', '')
145 f45a7ac4 Christos Stavrakakis
        net = nic['network']
146 b578d9e7 Christos Stavrakakis
        if ipv4:
147 b578d9e7 Christos Stavrakakis
            net.reserve_address(ipv4)
148 b578d9e7 Christos Stavrakakis
149 b578d9e7 Christos Stavrakakis
        nic['dirty'] = False
150 b578d9e7 Christos Stavrakakis
        vm.nics.create(**nic)
151 b578d9e7 Christos Stavrakakis
        # Dummy save the network, because UI uses changed-since for VMs
152 b578d9e7 Christos Stavrakakis
        # and Networks in order to show the VM NICs
153 b578d9e7 Christos Stavrakakis
        net.save()
154 b578d9e7 Christos Stavrakakis
155 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
156 b578d9e7 Christos Stavrakakis
    vm.save()
157 b578d9e7 Christos Stavrakakis
158 b578d9e7 Christos Stavrakakis
159 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
160 b578d9e7 Christos Stavrakakis
    """Process NIC dict from ganeti hooks."""
161 b578d9e7 Christos Stavrakakis
    new_nics = []
162 b578d9e7 Christos Stavrakakis
    for i, new_nic in enumerate(ganeti_nics):
163 77f0fa63 Christos Stavrakakis
        network = new_nic.get('network', '')
164 22ee6892 Christos Stavrakakis
        n = str(network)
165 77f0fa63 Christos Stavrakakis
        pk = utils.id_from_network_name(n)
166 77f0fa63 Christos Stavrakakis
167 fdc94944 Christos Stavrakakis
        net = Network.objects.get(pk=pk)
168 77f0fa63 Christos Stavrakakis
169 77f0fa63 Christos Stavrakakis
        # Get the new nic info
170 77f0fa63 Christos Stavrakakis
        mac = new_nic.get('mac', '')
171 77f0fa63 Christos Stavrakakis
        ipv4 = new_nic.get('ip', '')
172 fd95834e Christos Stavrakakis
        if net.subnet6:
173 fd95834e Christos Stavrakakis
            ipv6 = mac2eui64(mac, net.subnet6)
174 fd95834e Christos Stavrakakis
        else:
175 fd95834e Christos Stavrakakis
            ipv6 = ''
176 9afeb669 Kostas Papadimitriou
177 77f0fa63 Christos Stavrakakis
        firewall = new_nic.get('firewall', '')
178 658a825a Giorgos Verigakis
        firewall_profile = _reverse_tags.get(firewall, '')
179 658a825a Giorgos Verigakis
        if not firewall_profile and net.public:
180 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
181 9afeb669 Kostas Papadimitriou
182 b578d9e7 Christos Stavrakakis
        nic = {
183 cc92b70f Christos Stavrakakis
            'index': i,
184 cc92b70f Christos Stavrakakis
            'network': net,
185 cc92b70f Christos Stavrakakis
            'mac': mac,
186 cc92b70f Christos Stavrakakis
            'ipv4': ipv4,
187 cc92b70f Christos Stavrakakis
            'ipv6': ipv6,
188 939d71dd Christos Stavrakakis
            'firewall_profile': firewall_profile,
189 939d71dd Christos Stavrakakis
            'state': 'ACTIVE'}
190 b578d9e7 Christos Stavrakakis
191 b578d9e7 Christos Stavrakakis
        new_nics.append(nic)
192 b578d9e7 Christos Stavrakakis
    return new_nics
193 b578d9e7 Christos Stavrakakis
194 b578d9e7 Christos Stavrakakis
195 b578d9e7 Christos Stavrakakis
def nics_changed(old_nics, new_nics):
196 b578d9e7 Christos Stavrakakis
    """Return True if NICs have changed in any way."""
197 b578d9e7 Christos Stavrakakis
    if len(old_nics) != len(new_nics):
198 b578d9e7 Christos Stavrakakis
        return True
199 be4d8aed Christos Stavrakakis
    fields = ["ipv4", "ipv6", "mac", "firewall_profile", "index", "network"]
200 b578d9e7 Christos Stavrakakis
    for old_nic, new_nic in zip(old_nics, new_nics):
201 be4d8aed Christos Stavrakakis
        for field in fields:
202 be4d8aed Christos Stavrakakis
            if getattr(old_nic, field) != new_nic[field]:
203 be4d8aed Christos Stavrakakis
                return True
204 b578d9e7 Christos Stavrakakis
    return False
205 22ee6892 Christos Stavrakakis
206 22ee6892 Christos Stavrakakis
207 bd392934 Christos Stavrakakis
def release_instance_nics(vm):
208 bd392934 Christos Stavrakakis
    for nic in vm.nics.all():
209 40ef487d Christos Stavrakakis
        net = nic.network
210 fa454545 Christos Stavrakakis
        if nic.ipv4:
211 40ef487d Christos Stavrakakis
            net.release_address(nic.ipv4)
212 bd392934 Christos Stavrakakis
        nic.delete()
213 40ef487d Christos Stavrakakis
        net.save()
214 bd392934 Christos Stavrakakis
215 bd392934 Christos Stavrakakis
216 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
217 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
218 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
219 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
220 22ee6892 Christos Stavrakakis
221 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
222 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
223 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
224 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
225 22ee6892 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 e97288bc Christos Stavrakakis
        if status == 'success' or (status == 'error' and
237 e97288bc Christos Stavrakakis
                                   back_network.operstate == 'ERROR'):
238 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
239 e97288bc Christos Stavrakakis
            back_network.deleted = True
240 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
241 22ee6892 Christos Stavrakakis
242 e97288bc Christos Stavrakakis
    if status == 'success':
243 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
244 22ee6892 Christos Stavrakakis
    back_network.save()
245 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
246 4d5d0b9c Christos Stavrakakis
    update_network_state(back_network.network)
247 fd2bdbb2 Christos Stavrakakis
248 fd2bdbb2 Christos Stavrakakis
249 2509ce17 Christos Stavrakakis
def update_network_state(network):
250 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
251 cb4eee84 Christos Stavrakakis

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

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

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

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

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

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

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

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

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

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

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