Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (27.8 kB)

1 db3037f1 Giorgos Korfiatis
# Copyright 2011-2013 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 01f5f8d9 Christos Stavrakakis
from synnefo.logic.rapi import GanetiApiError, JOB_STATUS_FINALIZED
45 529178b1 Giorgos Verigakis
46 3524241a Christos Stavrakakis
from logging import getLogger
47 3524241a Christos Stavrakakis
log = getLogger(__name__)
48 9e98ba3c Giorgos Verigakis
49 529178b1 Giorgos Verigakis
50 efff6193 Giorgos Verigakis
_firewall_tags = {
51 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
52 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
53 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
54 efff6193 Giorgos Verigakis
55 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
56 efff6193 Giorgos Verigakis
57 02feca11 Vassilios Karakoidas
58 093f9c53 Vangelis Koukis
@transaction.commit_on_success
59 fd95834e Christos Stavrakakis
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None):
60 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
61 02feca11 Vassilios Karakoidas

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

66 02feca11 Vassilios Karakoidas
    """
67 41303ed0 Vangelis Koukis
    # See #1492, #1031, #1111 why this line has been removed
68 41303ed0 Vangelis Koukis
    #if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
69 fd65ab41 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
70 02feca11 Vassilios Karakoidas
        raise VirtualMachine.InvalidBackendMsgError(opcode, status)
71 02feca11 Vassilios Karakoidas
72 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = jobid
73 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = status
74 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = opcode
75 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = logmsg
76 02feca11 Vassilios Karakoidas
77 32a0b855 Giorgos Korfiatis
    # Update backendtime only for jobs that have been successfully completed,
78 32a0b855 Giorgos Korfiatis
    # since only these jobs update the state of the VM. Else a "race condition"
79 32a0b855 Giorgos Korfiatis
    # may occur when a successful job (e.g. OP_INSTANCE_REMOVE) completes
80 32a0b855 Giorgos Korfiatis
    # before an error job and messages arrive in reversed order.
81 32a0b855 Giorgos Korfiatis
    if status == 'success':
82 32a0b855 Giorgos Korfiatis
        vm.backendtime = etime
83 32a0b855 Giorgos Korfiatis
84 02feca11 Vassilios Karakoidas
    # Notifications of success change the operating state
85 41303ed0 Vangelis Koukis
    state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode, None)
86 41303ed0 Vangelis Koukis
    if status == 'success' and state_for_success is not None:
87 c4ce868e Christos Stavrakakis
        vm.operstate = state_for_success
88 685b219e Vangelis Koukis
89 fd95834e Christos Stavrakakis
    # Update the NICs of the VM
90 fd95834e Christos Stavrakakis
    if status == "success" and nics is not None:
91 fd95834e Christos Stavrakakis
        _process_net_status(vm, etime, nics)
92 fd95834e Christos Stavrakakis
93 91954b45 Christos Stavrakakis
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
94 cb4eee84 Christos Stavrakakis
    if opcode == 'OP_INSTANCE_CREATE' and status in ('canceled', 'error'):
95 c4ce868e Christos Stavrakakis
        vm.operstate = 'ERROR'
96 c4ce868e Christos Stavrakakis
        vm.backendtime = etime
97 cb4eee84 Christos Stavrakakis
    elif opcode == 'OP_INSTANCE_REMOVE':
98 e97288bc Christos Stavrakakis
        # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR,
99 e97288bc Christos Stavrakakis
        # when no instance exists at the Ganeti backend.
100 198d91c3 Christos Stavrakakis
        if status == "success" or (status == "error" and
101 198d91c3 Christos Stavrakakis
                                   not vm_exists_in_backend(vm)):
102 40d53b77 Christos Stavrakakis
            _process_net_status(vm, etime, nics=[])
103 e97288bc Christos Stavrakakis
            vm.operstate = state_for_success
104 e97288bc Christos Stavrakakis
            vm.backendtime = etime
105 25891abc Christos Stavrakakis
            if not vm.deleted:
106 25891abc Christos Stavrakakis
                vm.deleted = True
107 25891abc Christos Stavrakakis
                # Issue and accept commission to Quotaholder
108 db3037f1 Giorgos Korfiatis
                quotas.issue_and_accept_commission(vm, delete=True)
109 32a0b855 Giorgos Korfiatis
                # the above has already saved the object and committed;
110 32a0b855 Giorgos Korfiatis
                # a second save would override others' changes, since the
111 32a0b855 Giorgos Korfiatis
                # object is now unlocked
112 32a0b855 Giorgos Korfiatis
                return
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 05146623 Christos Stavrakakis
    network = back_network.network
227 05146623 Christos Stavrakakis
228 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
229 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
230 22ee6892 Christos Stavrakakis
    if status == 'success' and state_for_success is not None:
231 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
232 22ee6892 Christos Stavrakakis
233 e6f6627c Christos Stavrakakis
    if status in ('canceled', 'error') and opcode == 'OP_NETWORK_ADD':
234 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
235 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
236 22ee6892 Christos Stavrakakis
237 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
238 198d91c3 Christos Stavrakakis
        network_is_deleted = (status == "success")
239 198d91c3 Christos Stavrakakis
        if network_is_deleted or (status == "error" and not
240 198d91c3 Christos Stavrakakis
                                  network_exists_in_backend(back_network)):
241 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
242 e97288bc Christos Stavrakakis
            back_network.deleted = True
243 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
244 22ee6892 Christos Stavrakakis
245 e97288bc Christos Stavrakakis
    if status == 'success':
246 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
247 22ee6892 Christos Stavrakakis
    back_network.save()
248 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
249 05146623 Christos Stavrakakis
    update_network_state(network)
250 fd2bdbb2 Christos Stavrakakis
251 fd2bdbb2 Christos Stavrakakis
252 2509ce17 Christos Stavrakakis
def update_network_state(network):
253 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
254 cb4eee84 Christos Stavrakakis

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

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

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

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

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

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

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

730 1a894bfe Christos Stavrakakis
    """
731 1a894bfe Christos Stavrakakis
    nodes = get_ganeti_nodes(backend, bulk=True)
732 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
733 1a894bfe Christos Stavrakakis
    res = {}
734 1a894bfe Christos Stavrakakis
    for a in attr:
735 1a894bfe Christos Stavrakakis
        res[a] = 0
736 1a894bfe Christos Stavrakakis
    for n in nodes:
737 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
738 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
739 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
740 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
741 1a894bfe Christos Stavrakakis
            for a in attr:
742 1a894bfe Christos Stavrakakis
                res[a] += int(n[a])
743 1a894bfe Christos Stavrakakis
    return res
744 1a894bfe Christos Stavrakakis
745 1a894bfe Christos Stavrakakis
746 1a894bfe Christos Stavrakakis
def update_resources(backend, resources=None):
747 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
748 1a894bfe Christos Stavrakakis

749 1a894bfe Christos Stavrakakis
    """
750 17852fe9 Giorgos Verigakis
751 1a894bfe Christos Stavrakakis
    if not resources:
752 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
753 41303ed0 Vangelis Koukis
754 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
755 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
756 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
757 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
758 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
759 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
760 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
761 1a894bfe Christos Stavrakakis
    backend.save()
762 1a894bfe Christos Stavrakakis
763 1a894bfe Christos Stavrakakis
764 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
765 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
766 1a894bfe Christos Stavrakakis

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

770 1a894bfe Christos Stavrakakis
    """
771 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
772 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
773 1a894bfe Christos Stavrakakis
    mem = 0
774 1a894bfe Christos Stavrakakis
    for i in instances:
775 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
776 1a894bfe Christos Stavrakakis
    return mem
777 b3d28af2 Christos Stavrakakis
778 b3d28af2 Christos Stavrakakis
##
779 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
780 b3d28af2 Christos Stavrakakis
##
781 b3d28af2 Christos Stavrakakis
782 b3d28af2 Christos Stavrakakis
783 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
784 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
785 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
786 b3d28af2 Christos Stavrakakis
        return result
787 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
788 b3d28af2 Christos Stavrakakis
    return result
789 b3d28af2 Christos Stavrakakis
790 b3d28af2 Christos Stavrakakis
791 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
792 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
793 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
794 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
795 3524241a Christos Stavrakakis
    return result
796 b3d28af2 Christos Stavrakakis
797 b3d28af2 Christos Stavrakakis
798 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
799 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
800 3524241a Christos Stavrakakis
        for group in client.GetGroups():
801 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
802 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
803 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
804 3524241a Christos Stavrakakis
            if result[0] != 'success':
805 3524241a Christos Stavrakakis
                return result
806 b3d28af2 Christos Stavrakakis
807 b3d28af2 Christos Stavrakakis
    return result
808 b3d28af2 Christos Stavrakakis
809 b3d28af2 Christos Stavrakakis
810 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
811 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
812 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
813 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
814 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
815 cc92b70f Christos Stavrakakis
                                         [result], None)
816 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
817 b3d28af2 Christos Stavrakakis
818 b3d28af2 Christos Stavrakakis
    if status == 'success':
819 b3d28af2 Christos Stavrakakis
        return (status, None)
820 b3d28af2 Christos Stavrakakis
    else:
821 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
822 b3d28af2 Christos Stavrakakis
        return (status, error)