Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (35.6 kB)

1 41a7fae7 Christos Stavrakakis
# 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 72dea98f Christos Stavrakakis
                               FloatingIP,
39 3524241a Christos Stavrakakis
                               BackendNetwork, BACKEND_STATUSES,
40 ca4d59e3 Christos Stavrakakis
                               pooled_rapi_client, VirtualMachineDiagnostic,
41 ca4d59e3 Christos Stavrakakis
                               Flavor)
42 03992c72 Christos Stavrakakis
from synnefo.logic import utils
43 cb4eee84 Christos Stavrakakis
from synnefo import quotas
44 b7d38981 Dimitris Aragiorgis
from synnefo.api.util import release_resource
45 fd95834e Christos Stavrakakis
from synnefo.util.mac2eui64 import mac2eui64
46 198d91c3 Christos Stavrakakis
from synnefo.logic.rapi import GanetiApiError
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 41a7fae7 Christos Stavrakakis
def handle_vm_quotas(vm, job_id, job_opcode, job_status, job_fields):
61 41a7fae7 Christos Stavrakakis
    """Handle quotas for updated VirtualMachine.
62 41a7fae7 Christos Stavrakakis

63 41a7fae7 Christos Stavrakakis
    Update quotas for the updated VirtualMachine based on the job that run on
64 41a7fae7 Christos Stavrakakis
    the Ganeti backend. If a commission has been already issued for this job,
65 41a7fae7 Christos Stavrakakis
    then this commission is just accepted or rejected based on the job status.
66 41a7fae7 Christos Stavrakakis
    Otherwise, a new commission for the given change is issued, that is also in
67 41a7fae7 Christos Stavrakakis
    force and auto-accept mode. In this case, previous commissions are
68 41a7fae7 Christos Stavrakakis
    rejected, since they reflect a previous state of the VM.
69 41a7fae7 Christos Stavrakakis

70 41a7fae7 Christos Stavrakakis
    """
71 41a7fae7 Christos Stavrakakis
    if job_status not in ["success", "error", "canceled"]:
72 88fd91af Christos Stavrakakis
        return vm
73 41a7fae7 Christos Stavrakakis
74 41a7fae7 Christos Stavrakakis
    # Check successful completion of a job will trigger any quotable change in
75 41a7fae7 Christos Stavrakakis
    # the VM state.
76 41a7fae7 Christos Stavrakakis
    action = utils.get_action_from_opcode(job_opcode, job_fields)
77 88fd91af Christos Stavrakakis
    if action == "BUILD":
78 88fd91af Christos Stavrakakis
        # Quotas for new VMs are automatically accepted by the API
79 88fd91af Christos Stavrakakis
        return vm
80 41a7fae7 Christos Stavrakakis
    commission_info = quotas.get_commission_info(vm, action=action,
81 41a7fae7 Christos Stavrakakis
                                                 action_fields=job_fields)
82 41a7fae7 Christos Stavrakakis
83 41a7fae7 Christos Stavrakakis
    if vm.task_job_id == job_id and vm.serial is not None:
84 41a7fae7 Christos Stavrakakis
        # Commission for this change has already been issued. So just
85 562bf712 Christos Stavrakakis
        # accept/reject it. Special case is OP_INSTANCE_CREATE, which even
86 562bf712 Christos Stavrakakis
        # if fails, must be accepted, as the user must manually remove the
87 562bf712 Christos Stavrakakis
        # failed server
88 41a7fae7 Christos Stavrakakis
        serial = vm.serial
89 88fd91af Christos Stavrakakis
        if job_status == "success":
90 41a7fae7 Christos Stavrakakis
            quotas.accept_serial(serial)
91 41a7fae7 Christos Stavrakakis
        elif job_status in ["error", "canceled"]:
92 41a7fae7 Christos Stavrakakis
            log.debug("Job %s failed. Rejecting related serial %s", job_id,
93 41a7fae7 Christos Stavrakakis
                      serial)
94 41a7fae7 Christos Stavrakakis
            quotas.reject_serial(serial)
95 41a7fae7 Christos Stavrakakis
        vm.serial = None
96 41a7fae7 Christos Stavrakakis
    elif job_status == "success" and commission_info is not None:
97 41a7fae7 Christos Stavrakakis
        log.debug("Expected job was %s. Processing job %s. Commission for"
98 41a7fae7 Christos Stavrakakis
                  " this job: %s", vm.task_job_id, job_id, commission_info)
99 41a7fae7 Christos Stavrakakis
        # Commission for this change has not been issued, or the issued
100 41a7fae7 Christos Stavrakakis
        # commission was unaware of the current change. Reject all previous
101 41a7fae7 Christos Stavrakakis
        # commissions and create a new one in forced mode!
102 9122ffab Christos Stavrakakis
        commission_name = ("client: dispatcher, resource: %s, ganeti_job: %s"
103 9122ffab Christos Stavrakakis
                           % (vm, job_id))
104 5c8076b6 Christos Stavrakakis
        quotas.handle_resource_commission(vm, action,
105 5c8076b6 Christos Stavrakakis
                                          commission_info=commission_info,
106 5c8076b6 Christos Stavrakakis
                                          commission_name=commission_name,
107 5c8076b6 Christos Stavrakakis
                                          force=True,
108 5c8076b6 Christos Stavrakakis
                                          auto_accept=True)
109 5c8076b6 Christos Stavrakakis
        log.debug("Issued new commission: %s", vm.serial)
110 41a7fae7 Christos Stavrakakis
111 41a7fae7 Christos Stavrakakis
    return vm
112 41a7fae7 Christos Stavrakakis
113 41a7fae7 Christos Stavrakakis
114 093f9c53 Vangelis Koukis
@transaction.commit_on_success
115 ca4d59e3 Christos Stavrakakis
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None,
116 ca4d59e3 Christos Stavrakakis
                      beparams=None):
117 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
118 02feca11 Vassilios Karakoidas

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

123 02feca11 Vassilios Karakoidas
    """
124 41303ed0 Vangelis Koukis
    # See #1492, #1031, #1111 why this line has been removed
125 41303ed0 Vangelis Koukis
    #if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
126 fd65ab41 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
127 02feca11 Vassilios Karakoidas
        raise VirtualMachine.InvalidBackendMsgError(opcode, status)
128 02feca11 Vassilios Karakoidas
129 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = jobid
130 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = status
131 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = opcode
132 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = logmsg
133 02feca11 Vassilios Karakoidas
134 41a7fae7 Christos Stavrakakis
    if status in ["queued", "waiting", "running"]:
135 41a7fae7 Christos Stavrakakis
        vm.save()
136 41a7fae7 Christos Stavrakakis
        return
137 41a7fae7 Christos Stavrakakis
138 41a7fae7 Christos Stavrakakis
    state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode)
139 32a0b855 Giorgos Korfiatis
140 02feca11 Vassilios Karakoidas
    # Notifications of success change the operating state
141 41a7fae7 Christos Stavrakakis
    if status == "success":
142 41a7fae7 Christos Stavrakakis
        if state_for_success is not None:
143 41a7fae7 Christos Stavrakakis
            vm.operstate = state_for_success
144 41a7fae7 Christos Stavrakakis
        if beparams:
145 41a7fae7 Christos Stavrakakis
            # Change the flavor of the VM
146 ca4d59e3 Christos Stavrakakis
            _process_resize(vm, beparams)
147 41a7fae7 Christos Stavrakakis
        # Update backendtime only for jobs that have been successfully
148 41a7fae7 Christos Stavrakakis
        # completed, since only these jobs update the state of the VM. Else a
149 41a7fae7 Christos Stavrakakis
        # "race condition" may occur when a successful job (e.g.
150 41a7fae7 Christos Stavrakakis
        # OP_INSTANCE_REMOVE) completes before an error job and messages arrive
151 41a7fae7 Christos Stavrakakis
        # in reversed order.
152 41a7fae7 Christos Stavrakakis
        vm.backendtime = etime
153 ca4d59e3 Christos Stavrakakis
154 90858bda Christos Stavrakakis
    if status in ["success", "error", "canceled"] and nics is not None:
155 90858bda Christos Stavrakakis
        # Update the NICs of the VM
156 90858bda Christos Stavrakakis
        _process_net_status(vm, etime, nics)
157 90858bda Christos Stavrakakis
158 91954b45 Christos Stavrakakis
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
159 cb4eee84 Christos Stavrakakis
    if opcode == 'OP_INSTANCE_CREATE' and status in ('canceled', 'error'):
160 c4ce868e Christos Stavrakakis
        vm.operstate = 'ERROR'
161 c4ce868e Christos Stavrakakis
        vm.backendtime = etime
162 cb4eee84 Christos Stavrakakis
    elif opcode == 'OP_INSTANCE_REMOVE':
163 e97288bc Christos Stavrakakis
        # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR,
164 e97288bc Christos Stavrakakis
        # when no instance exists at the Ganeti backend.
165 e97288bc Christos Stavrakakis
        # See ticket #799 for all the details.
166 ed2064f8 Christos Stavrakakis
        if status == 'success' or (status == 'error' and
167 198d91c3 Christos Stavrakakis
                                   not vm_exists_in_backend(vm)):
168 72dea98f Christos Stavrakakis
            # VM has been deleted. Release the instance IPs
169 72dea98f Christos Stavrakakis
            release_instance_ips(vm, [])
170 72dea98f Christos Stavrakakis
            # And delete the releated NICs (must be performed after release!)
171 72dea98f Christos Stavrakakis
            vm.nics.all().delete()
172 e97288bc Christos Stavrakakis
            vm.deleted = True
173 e97288bc Christos Stavrakakis
            vm.operstate = state_for_success
174 e97288bc Christos Stavrakakis
            vm.backendtime = etime
175 41a7fae7 Christos Stavrakakis
            status = "success"
176 41a7fae7 Christos Stavrakakis
177 41a7fae7 Christos Stavrakakis
    if status in ["success", "error", "canceled"]:
178 41a7fae7 Christos Stavrakakis
        # Job is finalized: Handle quotas/commissioning
179 41a7fae7 Christos Stavrakakis
        job_fields = {"nics": nics, "beparams": beparams}
180 41a7fae7 Christos Stavrakakis
        vm = handle_vm_quotas(vm, job_id=jobid, job_opcode=opcode,
181 41a7fae7 Christos Stavrakakis
                              job_status=status, job_fields=job_fields)
182 41a7fae7 Christos Stavrakakis
        # and clear task fields
183 41a7fae7 Christos Stavrakakis
        if vm.task_job_id == jobid:
184 41a7fae7 Christos Stavrakakis
            vm.task = None
185 41a7fae7 Christos Stavrakakis
            vm.task_job_id = None
186 02feca11 Vassilios Karakoidas
187 02feca11 Vassilios Karakoidas
    vm.save()
188 22e52ede Vassilios Karakoidas
189 ad2d6807 Vangelis Koukis
190 ca4d59e3 Christos Stavrakakis
def _process_resize(vm, beparams):
191 ca4d59e3 Christos Stavrakakis
    """Change flavor of a VirtualMachine based on new beparams."""
192 ca4d59e3 Christos Stavrakakis
    old_flavor = vm.flavor
193 41a7fae7 Christos Stavrakakis
    vcpus = beparams.get("vcpus", old_flavor.cpu)
194 41a7fae7 Christos Stavrakakis
    ram = beparams.get("maxmem", old_flavor.ram)
195 41a7fae7 Christos Stavrakakis
    if vcpus == old_flavor.cpu and ram == old_flavor.ram:
196 ca4d59e3 Christos Stavrakakis
        return
197 ca4d59e3 Christos Stavrakakis
    try:
198 ca4d59e3 Christos Stavrakakis
        new_flavor = Flavor.objects.get(cpu=vcpus, ram=ram,
199 ca4d59e3 Christos Stavrakakis
                                        disk=old_flavor.disk,
200 ca4d59e3 Christos Stavrakakis
                                        disk_template=old_flavor.disk_template)
201 ca4d59e3 Christos Stavrakakis
    except Flavor.DoesNotExist:
202 ca4d59e3 Christos Stavrakakis
        raise Exception("Can not find flavor for VM")
203 ca4d59e3 Christos Stavrakakis
    vm.flavor = new_flavor
204 ca4d59e3 Christos Stavrakakis
    vm.save()
205 ca4d59e3 Christos Stavrakakis
206 ca4d59e3 Christos Stavrakakis
207 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
208 c4e55622 Christos Stavrakakis
def process_net_status(vm, etime, nics):
209 fd95834e Christos Stavrakakis
    """Wrap _process_net_status inside transaction."""
210 fd95834e Christos Stavrakakis
    _process_net_status(vm, etime, nics)
211 fd95834e Christos Stavrakakis
212 fd95834e Christos Stavrakakis
213 fd95834e Christos Stavrakakis
def _process_net_status(vm, etime, nics):
214 ad2d6807 Vangelis Koukis
    """Process a net status notification from the backend
215 ad2d6807 Vangelis Koukis

216 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
217 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
218 ad2d6807 Vangelis Koukis

219 ad2d6807 Vangelis Koukis
    Update the state of the VM in the DB accordingly.
220 ad2d6807 Vangelis Koukis
    """
221 37ca953f Christodoulos Psaltis
222 b578d9e7 Christos Stavrakakis
    ganeti_nics = process_ganeti_nics(nics)
223 b578d9e7 Christos Stavrakakis
    if not nics_changed(vm.nics.order_by('index'), ganeti_nics):
224 b578d9e7 Christos Stavrakakis
        log.debug("NICs for VM %s have not changed", vm)
225 40d53b77 Christos Stavrakakis
        return
226 40d53b77 Christos Stavrakakis
227 40d53b77 Christos Stavrakakis
    # Get X-Lock on backend before getting X-Lock on network IP pools, to
228 40d53b77 Christos Stavrakakis
    # guarantee that no deadlock will occur with Backend allocator.
229 40d53b77 Christos Stavrakakis
    Backend.objects.select_for_update().get(id=vm.backend_id)
230 b578d9e7 Christos Stavrakakis
231 72dea98f Christos Stavrakakis
    # NICs have changed. Release the instance IPs
232 72dea98f Christos Stavrakakis
    release_instance_ips(vm, ganeti_nics)
233 72dea98f Christos Stavrakakis
    # And delete the releated NICs (must be performed after release!)
234 72dea98f Christos Stavrakakis
    vm.nics.all().delete()
235 77f0fa63 Christos Stavrakakis
236 b578d9e7 Christos Stavrakakis
    for nic in ganeti_nics:
237 8d325d4b Christos Stavrakakis
        ipv4 = nic["ipv4"]
238 f45a7ac4 Christos Stavrakakis
        net = nic['network']
239 b578d9e7 Christos Stavrakakis
        if ipv4:
240 b578d9e7 Christos Stavrakakis
            net.reserve_address(ipv4)
241 b578d9e7 Christos Stavrakakis
242 b578d9e7 Christos Stavrakakis
        nic['dirty'] = False
243 b578d9e7 Christos Stavrakakis
        vm.nics.create(**nic)
244 b578d9e7 Christos Stavrakakis
        # Dummy save the network, because UI uses changed-since for VMs
245 b578d9e7 Christos Stavrakakis
        # and Networks in order to show the VM NICs
246 b578d9e7 Christos Stavrakakis
        net.save()
247 b578d9e7 Christos Stavrakakis
248 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
249 b578d9e7 Christos Stavrakakis
    vm.save()
250 b578d9e7 Christos Stavrakakis
251 b578d9e7 Christos Stavrakakis
252 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
253 b578d9e7 Christos Stavrakakis
    """Process NIC dict from ganeti hooks."""
254 b578d9e7 Christos Stavrakakis
    new_nics = []
255 b578d9e7 Christos Stavrakakis
    for i, new_nic in enumerate(ganeti_nics):
256 77f0fa63 Christos Stavrakakis
        network = new_nic.get('network', '')
257 22ee6892 Christos Stavrakakis
        n = str(network)
258 77f0fa63 Christos Stavrakakis
        pk = utils.id_from_network_name(n)
259 77f0fa63 Christos Stavrakakis
260 fdc94944 Christos Stavrakakis
        net = Network.objects.get(pk=pk)
261 77f0fa63 Christos Stavrakakis
262 77f0fa63 Christos Stavrakakis
        # Get the new nic info
263 8d325d4b Christos Stavrakakis
        mac = new_nic.get('mac')
264 8d325d4b Christos Stavrakakis
        ipv4 = new_nic.get('ip')
265 8d325d4b Christos Stavrakakis
        ipv6 = mac2eui64(mac, net.subnet6) if net.subnet6 is not None else None
266 8d325d4b Christos Stavrakakis
267 8d325d4b Christos Stavrakakis
        firewall = new_nic.get('firewall')
268 8d325d4b Christos Stavrakakis
        firewall_profile = _reverse_tags.get(firewall)
269 658a825a Giorgos Verigakis
        if not firewall_profile and net.public:
270 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
271 9afeb669 Kostas Papadimitriou
272 b578d9e7 Christos Stavrakakis
        nic = {
273 cc92b70f Christos Stavrakakis
            'index': i,
274 cc92b70f Christos Stavrakakis
            'network': net,
275 cc92b70f Christos Stavrakakis
            'mac': mac,
276 cc92b70f Christos Stavrakakis
            'ipv4': ipv4,
277 cc92b70f Christos Stavrakakis
            'ipv6': ipv6,
278 939d71dd Christos Stavrakakis
            'firewall_profile': firewall_profile,
279 939d71dd Christos Stavrakakis
            'state': 'ACTIVE'}
280 b578d9e7 Christos Stavrakakis
281 b578d9e7 Christos Stavrakakis
        new_nics.append(nic)
282 b578d9e7 Christos Stavrakakis
    return new_nics
283 b578d9e7 Christos Stavrakakis
284 b578d9e7 Christos Stavrakakis
285 b578d9e7 Christos Stavrakakis
def nics_changed(old_nics, new_nics):
286 b578d9e7 Christos Stavrakakis
    """Return True if NICs have changed in any way."""
287 b578d9e7 Christos Stavrakakis
    if len(old_nics) != len(new_nics):
288 b578d9e7 Christos Stavrakakis
        return True
289 be4d8aed Christos Stavrakakis
    fields = ["ipv4", "ipv6", "mac", "firewall_profile", "index", "network"]
290 b578d9e7 Christos Stavrakakis
    for old_nic, new_nic in zip(old_nics, new_nics):
291 be4d8aed Christos Stavrakakis
        for field in fields:
292 be4d8aed Christos Stavrakakis
            if getattr(old_nic, field) != new_nic[field]:
293 be4d8aed Christos Stavrakakis
                return True
294 b578d9e7 Christos Stavrakakis
    return False
295 22ee6892 Christos Stavrakakis
296 22ee6892 Christos Stavrakakis
297 72dea98f Christos Stavrakakis
def release_instance_ips(vm, ganeti_nics):
298 72dea98f Christos Stavrakakis
    old_addresses = set(vm.nics.values_list("network", "ipv4"))
299 f8675683 Christos Stavrakakis
    new_addresses = set(map(lambda nic: (nic["network"].id, nic["ipv4"]),
300 72dea98f Christos Stavrakakis
                            ganeti_nics))
301 72dea98f Christos Stavrakakis
    to_release = old_addresses - new_addresses
302 72dea98f Christos Stavrakakis
    for (network_id, ipv4) in to_release:
303 72dea98f Christos Stavrakakis
        if ipv4:
304 0c50c760 Christos Stavrakakis
            # Get X-Lock before searching floating IP, to exclusively search
305 0c50c760 Christos Stavrakakis
            # and release floating IP. Otherwise you may release a floating IP
306 0c50c760 Christos Stavrakakis
            # that has been just reserved.
307 0c50c760 Christos Stavrakakis
            net = Network.objects.select_for_update().get(id=network_id)
308 0c50c760 Christos Stavrakakis
            if net.floating_ip_pool:
309 0c50c760 Christos Stavrakakis
                try:
310 0c50c760 Christos Stavrakakis
                    floating_ip = net.floating_ips.select_for_update()\
311 0c50c760 Christos Stavrakakis
                                                  .get(ipv4=ipv4, machine=vm,
312 0c50c760 Christos Stavrakakis
                                                       deleted=False)
313 0c50c760 Christos Stavrakakis
                    floating_ip.machine = None
314 0c50c760 Christos Stavrakakis
                    floating_ip.save()
315 0c50c760 Christos Stavrakakis
                except FloatingIP.DoesNotExist:
316 0c50c760 Christos Stavrakakis
                    net.release_address(ipv4)
317 0c50c760 Christos Stavrakakis
            else:
318 72dea98f Christos Stavrakakis
                net.release_address(ipv4)
319 bd392934 Christos Stavrakakis
320 bd392934 Christos Stavrakakis
321 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
322 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
323 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
324 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
325 22ee6892 Christos Stavrakakis
326 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
327 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
328 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
329 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
330 22ee6892 Christos Stavrakakis
331 05146623 Christos Stavrakakis
    network = back_network.network
332 05146623 Christos Stavrakakis
333 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
334 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
335 22ee6892 Christos Stavrakakis
    if status == 'success' and state_for_success is not None:
336 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
337 22ee6892 Christos Stavrakakis
338 e6f6627c Christos Stavrakakis
    if status in ('canceled', 'error') and opcode == 'OP_NETWORK_ADD':
339 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
340 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
341 22ee6892 Christos Stavrakakis
342 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
343 198d91c3 Christos Stavrakakis
        network_is_deleted = (status == "success")
344 198d91c3 Christos Stavrakakis
        if network_is_deleted or (status == "error" and not
345 198d91c3 Christos Stavrakakis
                                  network_exists_in_backend(back_network)):
346 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
347 e97288bc Christos Stavrakakis
            back_network.deleted = True
348 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
349 22ee6892 Christos Stavrakakis
350 e97288bc Christos Stavrakakis
    if status == 'success':
351 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
352 22ee6892 Christos Stavrakakis
    back_network.save()
353 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
354 05146623 Christos Stavrakakis
    update_network_state(network)
355 fd2bdbb2 Christos Stavrakakis
356 fd2bdbb2 Christos Stavrakakis
357 2509ce17 Christos Stavrakakis
def update_network_state(network):
358 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
359 cb4eee84 Christos Stavrakakis

360 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
361 99af08a4 Christos Stavrakakis
    backends that network exists.
362 99af08a4 Christos Stavrakakis

363 99af08a4 Christos Stavrakakis
    The state of the network is:
364 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
365 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
366 99af08a4 Christos Stavrakakis

367 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
368 99af08a4 Christos Stavrakakis
    quotas for the network.
369 99af08a4 Christos Stavrakakis

370 99af08a4 Christos Stavrakakis
    """
371 99af08a4 Christos Stavrakakis
    if network.deleted:
372 99af08a4 Christos Stavrakakis
        # Network has already been deleted. Just assert that state is also
373 99af08a4 Christos Stavrakakis
        # DELETED
374 99af08a4 Christos Stavrakakis
        if not network.state == "DELETED":
375 99af08a4 Christos Stavrakakis
            network.state = "DELETED"
376 99af08a4 Christos Stavrakakis
            network.save()
377 cb4eee84 Christos Stavrakakis
        return
378 cb4eee84 Christos Stavrakakis
379 99af08a4 Christos Stavrakakis
    backend_states = [s.operstate for s in network.backend_networks.all()]
380 27cda06b Christos Stavrakakis
    if not backend_states and network.action != "DESTROY":
381 99af08a4 Christos Stavrakakis
        if network.state != "ACTIVE":
382 99af08a4 Christos Stavrakakis
            network.state = "ACTIVE"
383 99af08a4 Christos Stavrakakis
            network.save()
384 99af08a4 Christos Stavrakakis
            return
385 99af08a4 Christos Stavrakakis
386 99af08a4 Christos Stavrakakis
    # Network is deleted when all BackendNetworks go to "DELETED" operstate
387 27cda06b Christos Stavrakakis
    deleted = reduce(lambda x, y: x == y and "DELETED", backend_states,
388 27cda06b Christos Stavrakakis
                     "DELETED")
389 99af08a4 Christos Stavrakakis
390 cb4eee84 Christos Stavrakakis
    # Release the resources on the deletion of the Network
391 99af08a4 Christos Stavrakakis
    if deleted:
392 cb4eee84 Christos Stavrakakis
        log.info("Network %r deleted. Releasing link %r mac_prefix %r",
393 cb4eee84 Christos Stavrakakis
                 network.id, network.mac_prefix, network.link)
394 cb4eee84 Christos Stavrakakis
        network.deleted = True
395 99af08a4 Christos Stavrakakis
        network.state = "DELETED"
396 b7d38981 Dimitris Aragiorgis
        if network.mac_prefix:
397 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["mac_prefix"] == "pool":
398 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="mac_prefix",
399 b7d38981 Dimitris Aragiorgis
                                 value=network.mac_prefix)
400 b7d38981 Dimitris Aragiorgis
        if network.link:
401 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["link"] == "pool":
402 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="bridge", value=network.link)
403 cb4eee84 Christos Stavrakakis
404 cb4eee84 Christos Stavrakakis
        # Issue commission
405 e18c1749 Christos Stavrakakis
        if network.userid:
406 2509ce17 Christos Stavrakakis
            quotas.issue_and_accept_commission(network, delete=True)
407 32a0b855 Giorgos Korfiatis
            # the above has already saved the object and committed;
408 32a0b855 Giorgos Korfiatis
            # a second save would override others' changes, since the
409 32a0b855 Giorgos Korfiatis
            # object is now unlocked
410 32a0b855 Giorgos Korfiatis
            return
411 e18c1749 Christos Stavrakakis
        elif not network.public:
412 e18c1749 Christos Stavrakakis
            log.warning("Network %s does not have an owner!", network.id)
413 cb4eee84 Christos Stavrakakis
    network.save()
414 cb4eee84 Christos Stavrakakis
415 cb4eee84 Christos Stavrakakis
416 fd2bdbb2 Christos Stavrakakis
@transaction.commit_on_success
417 fd2bdbb2 Christos Stavrakakis
def process_network_modify(back_network, etime, jobid, opcode, status,
418 fd2bdbb2 Christos Stavrakakis
                           add_reserved_ips, remove_reserved_ips):
419 fd2bdbb2 Christos Stavrakakis
    assert (opcode == "OP_NETWORK_SET_PARAMS")
420 fd2bdbb2 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
421 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
422 fd2bdbb2 Christos Stavrakakis
423 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobid = jobid
424 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobstatus = status
425 fd2bdbb2 Christos Stavrakakis
    back_network.opcode = opcode
426 fd2bdbb2 Christos Stavrakakis
427 fd2bdbb2 Christos Stavrakakis
    if add_reserved_ips or remove_reserved_ips:
428 fd2bdbb2 Christos Stavrakakis
        net = back_network.network
429 fd2bdbb2 Christos Stavrakakis
        pool = net.get_pool()
430 fd2bdbb2 Christos Stavrakakis
        if add_reserved_ips:
431 fd2bdbb2 Christos Stavrakakis
            for ip in add_reserved_ips:
432 fd2bdbb2 Christos Stavrakakis
                pool.reserve(ip, external=True)
433 fd2bdbb2 Christos Stavrakakis
        if remove_reserved_ips:
434 fd2bdbb2 Christos Stavrakakis
            for ip in remove_reserved_ips:
435 fd2bdbb2 Christos Stavrakakis
                pool.put(ip, external=True)
436 fd2bdbb2 Christos Stavrakakis
        pool.save()
437 fd2bdbb2 Christos Stavrakakis
438 fd2bdbb2 Christos Stavrakakis
    if status == 'success':
439 fd2bdbb2 Christos Stavrakakis
        back_network.backendtime = etime
440 fd2bdbb2 Christos Stavrakakis
    back_network.save()
441 ad2d6807 Vangelis Koukis
442 c25cc9ec Vangelis Koukis
443 9068cd85 Georgios Gousios
@transaction.commit_on_success
444 0827883e Nikos Skalkotos
def process_create_progress(vm, etime, progress):
445 9068cd85 Georgios Gousios
446 0827883e Nikos Skalkotos
    percentage = int(progress)
447 9068cd85 Georgios Gousios
448 af90d919 Vangelis Koukis
    # The percentage may exceed 100%, due to the way
449 0827883e Nikos Skalkotos
    # snf-image:copy-progress tracks bytes read by image handling processes
450 af90d919 Vangelis Koukis
    percentage = 100 if percentage > 100 else percentage
451 af90d919 Vangelis Koukis
    if percentage < 0:
452 af90d919 Vangelis Koukis
        raise ValueError("Percentage cannot be negative")
453 9068cd85 Georgios Gousios
454 7ec9558b Vangelis Koukis
    # FIXME: log a warning here, see #1033
455 7ec9558b Vangelis Koukis
#   if last_update > percentage:
456 7ec9558b Vangelis Koukis
#       raise ValueError("Build percentage should increase monotonically " \
457 7ec9558b Vangelis Koukis
#                        "(old = %d, new = %d)" % (last_update, percentage))
458 9068cd85 Georgios Gousios
459 c25cc9ec Vangelis Koukis
    # This assumes that no message of type 'ganeti-create-progress' is going to
460 c25cc9ec Vangelis Koukis
    # arrive once OP_INSTANCE_CREATE has succeeded for a Ganeti instance and
461 c25cc9ec Vangelis Koukis
    # the instance is STARTED.  What if the two messages are processed by two
462 c25cc9ec Vangelis Koukis
    # separate dispatcher threads, and the 'ganeti-op-status' message for
463 c25cc9ec Vangelis Koukis
    # successful creation gets processed before the 'ganeti-create-progress'
464 c25cc9ec Vangelis Koukis
    # message? [vkoukis]
465 c25cc9ec Vangelis Koukis
    #
466 c25cc9ec Vangelis Koukis
    #if not vm.operstate == 'BUILD':
467 c25cc9ec Vangelis Koukis
    #    raise VirtualMachine.IllegalState("VM is not in building state")
468 9068cd85 Georgios Gousios
469 c25cc9ec Vangelis Koukis
    vm.buildpercentage = percentage
470 c4e55622 Christos Stavrakakis
    vm.backendtime = etime
471 9068cd85 Georgios Gousios
    vm.save()
472 ad2d6807 Vangelis Koukis
473 c25cc9ec Vangelis Koukis
474 952b2a48 Christos Stavrakakis
@transaction.commit_on_success
475 7d43565f Kostas Papadimitriou
def create_instance_diagnostic(vm, message, source, level="DEBUG", etime=None,
476 cc92b70f Christos Stavrakakis
                               details=None):
477 7d43565f Kostas Papadimitriou
    """
478 7d43565f Kostas Papadimitriou
    Create virtual machine instance diagnostic entry.
479 7d43565f Kostas Papadimitriou

480 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
481 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
482 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
483 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
484 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
485 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
486 7d43565f Kostas Papadimitriou
    """
487 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
488 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
489 cc92b70f Christos Stavrakakis
                                                   message=message,
490 cc92b70f Christos Stavrakakis
                                                   details=details)
491 7d43565f Kostas Papadimitriou
492 7d43565f Kostas Papadimitriou
493 2c022086 Christos Stavrakakis
def create_instance(vm, nics, flavor, image):
494 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
495 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
496 7f691719 Christos Stavrakakis

497 3a9b3cde Giorgos Verigakis
        metadata value should be a dictionary.
498 3a9b3cde Giorgos Verigakis
    """
499 864bed43 Christos Stavrakakis
500 1c382247 Vangelis Koukis
    # Handle arguments to CreateInstance() as a dictionary,
501 1c382247 Vangelis Koukis
    # initialize it based on a deployment-specific value.
502 1c382247 Vangelis Koukis
    # This enables the administrator to override deployment-specific
503 c25cc9ec Vangelis Koukis
    # arguments, such as the disk template to use, name of os provider
504 1c382247 Vangelis Koukis
    # and hypervisor-specific parameters at will (see Synnefo #785, #835).
505 1c382247 Vangelis Koukis
    #
506 bd87213f Christos Stavrakakis
    kw = vm.backend.get_create_params()
507 1c382247 Vangelis Koukis
    kw['mode'] = 'create'
508 924d8085 Christos Stavrakakis
    kw['name'] = vm.backend_vm_id
509 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
510 296682fe Kostas Papadimitriou
511 2a599282 Christos Stavrakakis
    kw['disk_template'] = flavor.disk_template
512 d7841399 Christos Stavrakakis
    kw['disks'] = [{"size": flavor.disk * 1024}]
513 006c6249 Christos Stavrakakis
    provider = flavor.disk_provider
514 296682fe Kostas Papadimitriou
    if provider:
515 296682fe Kostas Papadimitriou
        kw['disks'][0]['provider'] = provider
516 b2222a7f Christos Stavrakakis
        kw['disks'][0]['origin'] = flavor.disk_origin
517 296682fe Kostas Papadimitriou
518 cb66110b Christos Stavrakakis
    kw['nics'] = [{"network": nic.network.backend_id, "ip": nic.ipv4}
519 cb66110b Christos Stavrakakis
                  for nic in nics]
520 cb66110b Christos Stavrakakis
    backend = vm.backend
521 cb66110b Christos Stavrakakis
    depend_jobs = []
522 cb66110b Christos Stavrakakis
    for nic in nics:
523 cb66110b Christos Stavrakakis
        network = Network.objects.select_for_update().get(id=nic.network.id)
524 cb66110b Christos Stavrakakis
        bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
525 cb66110b Christos Stavrakakis
                                                             network=network)
526 cb66110b Christos Stavrakakis
        if bnet.operstate != "ACTIVE":
527 cb66110b Christos Stavrakakis
            if network.public:
528 736b9244 Christos Stavrakakis
                msg = "Can not connect instance to network %s. Network is not"\
529 736b9244 Christos Stavrakakis
                      " ACTIVE in backend %s." % (network, backend)
530 736b9244 Christos Stavrakakis
                raise Exception(msg)
531 cb66110b Christos Stavrakakis
            else:
532 527c7a7b Christos Stavrakakis
                jobs = create_network(network, backend, connect=True)
533 527c7a7b Christos Stavrakakis
                if isinstance(jobs, list):
534 527c7a7b Christos Stavrakakis
                    depend_jobs.extend(jobs)
535 527c7a7b Christos Stavrakakis
                else:
536 527c7a7b Christos Stavrakakis
                    depend_jobs.append(jobs)
537 cb66110b Christos Stavrakakis
    kw["depends"] = [[job, ["success", "error", "canceled"]]
538 cb66110b Christos Stavrakakis
                     for job in depend_jobs]
539 cb66110b Christos Stavrakakis
540 bd87213f Christos Stavrakakis
    if vm.backend.use_hotplug():
541 341c818e Dimitris Aragiorgis
        kw['hotplug'] = True
542 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
543 1c382247 Vangelis Koukis
    # kw['os'] = settings.GANETI_OS_PROVIDER
544 1c382247 Vangelis Koukis
    kw['ip_check'] = False
545 1c382247 Vangelis Koukis
    kw['name_check'] = False
546 79b7dbb7 Christos Stavrakakis
547 1c382247 Vangelis Koukis
    # Do not specific a node explicitly, have
548 1c382247 Vangelis Koukis
    # Ganeti use an iallocator instead
549 f8d1eaf4 Kostas Papadimitriou
    #kw['pnode'] = rapi.GetNodes()[0]
550 79b7dbb7 Christos Stavrakakis
551 1c382247 Vangelis Koukis
    kw['dry_run'] = settings.TEST
552 41303ed0 Vangelis Koukis
553 2b1db26f Giorgos Verigakis
    kw['beparams'] = {
554 cc92b70f Christos Stavrakakis
        'auto_balance': True,
555 cc92b70f Christos Stavrakakis
        'vcpus': flavor.cpu,
556 cc92b70f Christos Stavrakakis
        'memory': flavor.ram}
557 79b7dbb7 Christos Stavrakakis
558 e3b5be49 Giorgos Verigakis
    kw['osparams'] = {
559 79b7dbb7 Christos Stavrakakis
        'config_url': vm.config_url,
560 79b7dbb7 Christos Stavrakakis
        # Store image id and format to Ganeti
561 2a599282 Christos Stavrakakis
        'img_id': image['backend_id'],
562 3a9b3cde Giorgos Verigakis
        'img_format': image['format']}
563 d1eaa651 Christos Stavrakakis
564 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
565 1c382247 Vangelis Koukis
    # kw['hvparams'] = dict(serial_console=False)
566 79b7dbb7 Christos Stavrakakis
567 6afeb85d Christos Stavrakakis
    log.debug("Creating instance %s", utils.hide_pass(kw))
568 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
569 3524241a Christos Stavrakakis
        return client.CreateInstance(**kw)
570 f533f224 Vangelis Koukis
571 529178b1 Giorgos Verigakis
572 529178b1 Giorgos Verigakis
def delete_instance(vm):
573 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
574 3524241a Christos Stavrakakis
        return client.DeleteInstance(vm.backend_vm_id, dry_run=settings.TEST)
575 529178b1 Giorgos Verigakis
576 ad2d6807 Vangelis Koukis
577 529178b1 Giorgos Verigakis
def reboot_instance(vm, reboot_type):
578 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
579 bbae3e45 Christos Stavrakakis
    kwargs = {"instance": vm.backend_vm_id,
580 bbae3e45 Christos Stavrakakis
              "reboot_type": "hard"}
581 bbae3e45 Christos Stavrakakis
    # XXX: Currently shutdown_timeout parameter is not supported from the
582 bbae3e45 Christos Stavrakakis
    # Ganeti RAPI. Until supported, we will fallback for both reboot types
583 bbae3e45 Christos Stavrakakis
    # to the default shutdown timeout of Ganeti (120s). Note that reboot
584 bbae3e45 Christos Stavrakakis
    # type of Ganeti job must be always hard. The 'soft' and 'hard' type
585 bbae3e45 Christos Stavrakakis
    # of OS API is different from the one in Ganeti, and maps to
586 bbae3e45 Christos Stavrakakis
    # 'shutdown_timeout'.
587 bbae3e45 Christos Stavrakakis
    #if reboot_type == "hard":
588 bbae3e45 Christos Stavrakakis
    #    kwargs["shutdown_timeout"] = 0
589 bbae3e45 Christos Stavrakakis
    if settings.TEST:
590 bbae3e45 Christos Stavrakakis
        kwargs["dry_run"] = True
591 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
592 bbae3e45 Christos Stavrakakis
        return client.RebootInstance(**kwargs)
593 529178b1 Giorgos Verigakis
594 ad2d6807 Vangelis Koukis
595 529178b1 Giorgos Verigakis
def startup_instance(vm):
596 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
597 3524241a Christos Stavrakakis
        return client.StartupInstance(vm.backend_vm_id, dry_run=settings.TEST)
598 529178b1 Giorgos Verigakis
599 ad2d6807 Vangelis Koukis
600 529178b1 Giorgos Verigakis
def shutdown_instance(vm):
601 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
602 3524241a Christos Stavrakakis
        return client.ShutdownInstance(vm.backend_vm_id, dry_run=settings.TEST)
603 529178b1 Giorgos Verigakis
604 ad2d6807 Vangelis Koukis
605 2cd3f389 Christos Stavrakakis
def resize_instance(vm, vcpus, memory):
606 2cd3f389 Christos Stavrakakis
    beparams = {"vcpus": int(vcpus),
607 2cd3f389 Christos Stavrakakis
                "minmem": int(memory),
608 2cd3f389 Christos Stavrakakis
                "maxmem": int(memory)}
609 2cd3f389 Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
610 2cd3f389 Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, beparams=beparams)
611 2cd3f389 Christos Stavrakakis
612 2cd3f389 Christos Stavrakakis
613 529178b1 Giorgos Verigakis
def get_instance_console(vm):
614 71099804 Vangelis Koukis
    # RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
615 71099804 Vangelis Koukis
    # which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
616 71099804 Vangelis Koukis
    # useless (see #783).
617 71099804 Vangelis Koukis
    #
618 71099804 Vangelis Koukis
    # Until this is fixed on the Ganeti side, construct a console info reply
619 71099804 Vangelis Koukis
    # directly.
620 9afeb669 Kostas Papadimitriou
    #
621 71099804 Vangelis Koukis
    # WARNING: This assumes that VNC runs on port network_port on
622 71099804 Vangelis Koukis
    #          the instance's primary node, and is probably
623 71099804 Vangelis Koukis
    #          hypervisor-specific.
624 71099804 Vangelis Koukis
    #
625 bf5c82dc Christos Stavrakakis
    log.debug("Getting console for vm %s", vm)
626 bf5c82dc Christos Stavrakakis
627 71099804 Vangelis Koukis
    console = {}
628 71099804 Vangelis Koukis
    console['kind'] = 'vnc'
629 3524241a Christos Stavrakakis
630 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
631 3524241a Christos Stavrakakis
        i = client.GetInstance(vm.backend_vm_id)
632 3524241a Christos Stavrakakis
633 bd87213f Christos Stavrakakis
    if vm.backend.hypervisor == "kvm" and i['hvparams']['serial_console']:
634 71099804 Vangelis Koukis
        raise Exception("hv parameter serial_console cannot be true")
635 71099804 Vangelis Koukis
    console['host'] = i['pnode']
636 71099804 Vangelis Koukis
    console['port'] = i['network_port']
637 9afeb669 Kostas Papadimitriou
638 71099804 Vangelis Koukis
    return console
639 604b2bf8 Georgios Gousios
640 604b2bf8 Georgios Gousios
641 3524241a Christos Stavrakakis
def get_instance_info(vm):
642 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
643 198d91c3 Christos Stavrakakis
        return client.GetInstance(vm.backend_vm_id)
644 198d91c3 Christos Stavrakakis
645 198d91c3 Christos Stavrakakis
646 198d91c3 Christos Stavrakakis
def vm_exists_in_backend(vm):
647 198d91c3 Christos Stavrakakis
    try:
648 198d91c3 Christos Stavrakakis
        get_instance_info(vm)
649 198d91c3 Christos Stavrakakis
        return True
650 198d91c3 Christos Stavrakakis
    except GanetiApiError as e:
651 198d91c3 Christos Stavrakakis
        if e.code == 404:
652 198d91c3 Christos Stavrakakis
            return False
653 198d91c3 Christos Stavrakakis
        raise e
654 198d91c3 Christos Stavrakakis
655 198d91c3 Christos Stavrakakis
656 198d91c3 Christos Stavrakakis
def get_network_info(backend_network):
657 198d91c3 Christos Stavrakakis
    with pooled_rapi_client(backend_network) as client:
658 198d91c3 Christos Stavrakakis
        return client.GetNetwork(backend_network.network.backend_id)
659 198d91c3 Christos Stavrakakis
660 198d91c3 Christos Stavrakakis
661 198d91c3 Christos Stavrakakis
def network_exists_in_backend(backend_network):
662 198d91c3 Christos Stavrakakis
    try:
663 198d91c3 Christos Stavrakakis
        get_network_info(backend_network)
664 198d91c3 Christos Stavrakakis
        return True
665 198d91c3 Christos Stavrakakis
    except GanetiApiError as e:
666 198d91c3 Christos Stavrakakis
        if e.code == 404:
667 198d91c3 Christos Stavrakakis
            return False
668 f533f224 Vangelis Koukis
669 c25cc9ec Vangelis Koukis
670 99af08a4 Christos Stavrakakis
def create_network(network, backend, connect=True):
671 99af08a4 Christos Stavrakakis
    """Create a network in a Ganeti backend"""
672 99af08a4 Christos Stavrakakis
    log.debug("Creating network %s in backend %s", network, backend)
673 64938cb0 Giorgos Verigakis
674 99af08a4 Christos Stavrakakis
    job_id = _create_network(network, backend)
675 c25cc9ec Vangelis Koukis
676 99af08a4 Christos Stavrakakis
    if connect:
677 99af08a4 Christos Stavrakakis
        job_ids = connect_network(network, backend, depends=[job_id])
678 99af08a4 Christos Stavrakakis
        return job_ids
679 99af08a4 Christos Stavrakakis
    else:
680 99af08a4 Christos Stavrakakis
        return [job_id]
681 37ca953f Christodoulos Psaltis
682 64938cb0 Giorgos Verigakis
683 3524241a Christos Stavrakakis
def _create_network(network, backend):
684 3524241a Christos Stavrakakis
    """Create a network."""
685 c25cc9ec Vangelis Koukis
686 22ee6892 Christos Stavrakakis
    network_type = network.public and 'public' or 'private'
687 22ee6892 Christos Stavrakakis
688 22ee6892 Christos Stavrakakis
    tags = network.backend_tag
689 22ee6892 Christos Stavrakakis
    if network.dhcp:
690 22ee6892 Christos Stavrakakis
        tags.append('nfdhcpd')
691 2d762302 Dimitris Aragiorgis
692 2d762302 Dimitris Aragiorgis
    if network.public:
693 2d762302 Dimitris Aragiorgis
        conflicts_check = True
694 2d762302 Dimitris Aragiorgis
    else:
695 2d762302 Dimitris Aragiorgis
        conflicts_check = False
696 22ee6892 Christos Stavrakakis
697 5aeb4e93 Christos Stavrakakis
    # Use a dummy network subnet for IPv6 only networks. Currently Ganeti does
698 5aeb4e93 Christos Stavrakakis
    # not support IPv6 only networks. To bypass this limitation, we create the
699 5aeb4e93 Christos Stavrakakis
    # network with a dummy network subnet, and make Cyclades connect instances
700 5aeb4e93 Christos Stavrakakis
    # to such networks, with address=None.
701 5aeb4e93 Christos Stavrakakis
    subnet = network.subnet
702 5aeb4e93 Christos Stavrakakis
    if subnet is None:
703 5aeb4e93 Christos Stavrakakis
        subnet = "10.0.0.0/24"
704 5aeb4e93 Christos Stavrakakis
705 3524241a Christos Stavrakakis
    try:
706 3524241a Christos Stavrakakis
        bn = BackendNetwork.objects.get(network=network, backend=backend)
707 3524241a Christos Stavrakakis
        mac_prefix = bn.mac_prefix
708 3524241a Christos Stavrakakis
    except BackendNetwork.DoesNotExist:
709 cc92b70f Christos Stavrakakis
        raise Exception("BackendNetwork for network '%s' in backend '%s'"
710 3524241a Christos Stavrakakis
                        " does not exist" % (network.id, backend.id))
711 3524241a Christos Stavrakakis
712 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
713 3524241a Christos Stavrakakis
        return client.CreateNetwork(network_name=network.backend_id,
714 5aeb4e93 Christos Stavrakakis
                                    network=subnet,
715 6cc3a31c Christos Stavrakakis
                                    network6=network.subnet6,
716 3524241a Christos Stavrakakis
                                    gateway=network.gateway,
717 6cc3a31c Christos Stavrakakis
                                    gateway6=network.gateway6,
718 3524241a Christos Stavrakakis
                                    network_type=network_type,
719 3524241a Christos Stavrakakis
                                    mac_prefix=mac_prefix,
720 2d762302 Dimitris Aragiorgis
                                    conflicts_check=conflicts_check,
721 3524241a Christos Stavrakakis
                                    tags=tags)
722 3524241a Christos Stavrakakis
723 3524241a Christos Stavrakakis
724 99af08a4 Christos Stavrakakis
def connect_network(network, backend, depends=[], group=None):
725 3524241a Christos Stavrakakis
    """Connect a network to nodegroups."""
726 bf5c82dc Christos Stavrakakis
    log.debug("Connecting network %s to backend %s", network, backend)
727 bf5c82dc Christos Stavrakakis
728 2d762302 Dimitris Aragiorgis
    if network.public:
729 2d762302 Dimitris Aragiorgis
        conflicts_check = True
730 2d762302 Dimitris Aragiorgis
    else:
731 2d762302 Dimitris Aragiorgis
        conflicts_check = False
732 2d762302 Dimitris Aragiorgis
733 99af08a4 Christos Stavrakakis
    depends = [[job, ["success", "error", "canceled"]] for job in depends]
734 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
735 99af08a4 Christos Stavrakakis
        groups = [group] if group is not None else client.GetGroups()
736 99af08a4 Christos Stavrakakis
        job_ids = []
737 99af08a4 Christos Stavrakakis
        for group in groups:
738 99af08a4 Christos Stavrakakis
            job_id = client.ConnectNetwork(network.backend_id, group,
739 99af08a4 Christos Stavrakakis
                                           network.mode, network.link,
740 99af08a4 Christos Stavrakakis
                                           conflicts_check,
741 99af08a4 Christos Stavrakakis
                                           depends=depends)
742 99af08a4 Christos Stavrakakis
            job_ids.append(job_id)
743 99af08a4 Christos Stavrakakis
    return job_ids
744 22ee6892 Christos Stavrakakis
745 22ee6892 Christos Stavrakakis
746 99af08a4 Christos Stavrakakis
def delete_network(network, backend, disconnect=True):
747 99af08a4 Christos Stavrakakis
    log.debug("Deleting network %s from backend %s", network, backend)
748 22ee6892 Christos Stavrakakis
749 99af08a4 Christos Stavrakakis
    depends = []
750 99af08a4 Christos Stavrakakis
    if disconnect:
751 99af08a4 Christos Stavrakakis
        depends = disconnect_network(network, backend)
752 99af08a4 Christos Stavrakakis
    _delete_network(network, backend, depends=depends)
753 bf5c82dc Christos Stavrakakis
754 22ee6892 Christos Stavrakakis
755 99af08a4 Christos Stavrakakis
def _delete_network(network, backend, depends=[]):
756 99af08a4 Christos Stavrakakis
    depends = [[job, ["success", "error", "canceled"]] for job in depends]
757 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
758 99af08a4 Christos Stavrakakis
        return client.DeleteNetwork(network.backend_id, depends)
759 22ee6892 Christos Stavrakakis
760 22ee6892 Christos Stavrakakis
761 3524241a Christos Stavrakakis
def disconnect_network(network, backend, group=None):
762 bf5c82dc Christos Stavrakakis
    log.debug("Disconnecting network %s to backend %s", network, backend)
763 22ee6892 Christos Stavrakakis
764 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
765 99af08a4 Christos Stavrakakis
        groups = [group] if group is not None else client.GetGroups()
766 99af08a4 Christos Stavrakakis
        job_ids = []
767 99af08a4 Christos Stavrakakis
        for group in groups:
768 99af08a4 Christos Stavrakakis
            job_id = client.DisconnectNetwork(network.backend_id, group)
769 99af08a4 Christos Stavrakakis
            job_ids.append(job_id)
770 99af08a4 Christos Stavrakakis
    return job_ids
771 36f4cb29 Christos Stavrakakis
772 36f4cb29 Christos Stavrakakis
773 2a2b01e5 Christos Stavrakakis
def connect_to_network(vm, nic):
774 2a2b01e5 Christos Stavrakakis
    network = nic.network
775 99af08a4 Christos Stavrakakis
    backend = vm.backend
776 acfc71ef Christos Stavrakakis
    network = Network.objects.select_for_update().get(id=network.id)
777 99af08a4 Christos Stavrakakis
    bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
778 99af08a4 Christos Stavrakakis
                                                         network=network)
779 99af08a4 Christos Stavrakakis
    depend_jobs = []
780 99af08a4 Christos Stavrakakis
    if bnet.operstate != "ACTIVE":
781 99af08a4 Christos Stavrakakis
        depend_jobs = create_network(network, backend, connect=True)
782 99af08a4 Christos Stavrakakis
783 99af08a4 Christos Stavrakakis
    depends = [[job, ["success", "error", "canceled"]] for job in depend_jobs]
784 99af08a4 Christos Stavrakakis
785 2a2b01e5 Christos Stavrakakis
    nic = {'ip': nic.ipv4, 'network': network.backend_id}
786 22ee6892 Christos Stavrakakis
787 2a2b01e5 Christos Stavrakakis
    log.debug("Connecting NIC %s to VM %s", nic, vm)
788 22ee6892 Christos Stavrakakis
789 6488097c Christos Stavrakakis
    kwargs = {
790 6488097c Christos Stavrakakis
        "instance": vm.backend_vm_id,
791 6488097c Christos Stavrakakis
        "nics": [("add", nic)],
792 6488097c Christos Stavrakakis
        "depends": depends,
793 6488097c Christos Stavrakakis
    }
794 6488097c Christos Stavrakakis
    if vm.backend.use_hotplug():
795 6488097c Christos Stavrakakis
        kwargs["hotplug"] = True
796 6488097c Christos Stavrakakis
    if settings.TEST:
797 6488097c Christos Stavrakakis
        kwargs["dry_run"] = True
798 6488097c Christos Stavrakakis
799 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
800 6488097c Christos Stavrakakis
        return client.ModifyInstance(**kwargs)
801 22ee6892 Christos Stavrakakis
802 22ee6892 Christos Stavrakakis
803 3524241a Christos Stavrakakis
def disconnect_from_network(vm, nic):
804 bf5c82dc Christos Stavrakakis
    log.debug("Removing nic of VM %s, with index %s", vm, str(nic.index))
805 22ee6892 Christos Stavrakakis
806 6488097c Christos Stavrakakis
    kwargs = {
807 6488097c Christos Stavrakakis
        "instance": vm.backend_vm_id,
808 6488097c Christos Stavrakakis
        "nics": [("remove", nic.index, {})],
809 6488097c Christos Stavrakakis
    }
810 6488097c Christos Stavrakakis
    if vm.backend.use_hotplug():
811 6488097c Christos Stavrakakis
        kwargs["hotplug"] = True
812 6488097c Christos Stavrakakis
    if settings.TEST:
813 6488097c Christos Stavrakakis
        kwargs["dry_run"] = True
814 6488097c Christos Stavrakakis
815 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
816 6488097c Christos Stavrakakis
        jobID = client.ModifyInstance(**kwargs)
817 27435d42 Christos Stavrakakis
        # If the NIC has a tag for a firewall profile it must be deleted,
818 27435d42 Christos Stavrakakis
        # otherwise it may affect another NIC. XXX: Deleting the tag should
819 27435d42 Christos Stavrakakis
        # depend on the removing the NIC, but currently RAPI client does not
820 27435d42 Christos Stavrakakis
        # support this, this may result in clearing the firewall profile
821 27435d42 Christos Stavrakakis
        # without successfully removing the NIC. This issue will be fixed with
822 27435d42 Christos Stavrakakis
        # use of NIC UUIDs.
823 27435d42 Christos Stavrakakis
        firewall_profile = nic.firewall_profile
824 231b0fb6 Christos Stavrakakis
        if firewall_profile and firewall_profile != "DISABLED":
825 27435d42 Christos Stavrakakis
            tag = _firewall_tags[firewall_profile] % nic.index
826 27435d42 Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, [tag],
827 27435d42 Christos Stavrakakis
                                      dry_run=settings.TEST)
828 27435d42 Christos Stavrakakis
829 27435d42 Christos Stavrakakis
        return jobID
830 91826390 Giorgos Verigakis
831 c25cc9ec Vangelis Koukis
832 b2791a77 Christos Stavrakakis
def set_firewall_profile(vm, profile, index=0):
833 26563957 Giorgos Verigakis
    try:
834 b2791a77 Christos Stavrakakis
        tag = _firewall_tags[profile] % index
835 26563957 Giorgos Verigakis
    except KeyError:
836 91826390 Giorgos Verigakis
        raise ValueError("Unsopported Firewall Profile: %s" % profile)
837 37ca953f Christodoulos Psaltis
838 b2791a77 Christos Stavrakakis
    log.debug("Setting tag of VM %s, NIC index %d, to %s", vm, index, profile)
839 bf5c82dc Christos Stavrakakis
840 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
841 b2791a77 Christos Stavrakakis
        # Delete previous firewall tags
842 b2791a77 Christos Stavrakakis
        old_tags = client.GetInstanceTags(vm.backend_vm_id)
843 b2791a77 Christos Stavrakakis
        delete_tags = [(t % index) for t in _firewall_tags.values()
844 b2791a77 Christos Stavrakakis
                       if (t % index) in old_tags]
845 b2791a77 Christos Stavrakakis
        if delete_tags:
846 b2791a77 Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, delete_tags,
847 3524241a Christos Stavrakakis
                                      dry_run=settings.TEST)
848 37ca953f Christodoulos Psaltis
849 27435d42 Christos Stavrakakis
        if profile != "DISABLED":
850 27435d42 Christos Stavrakakis
            client.AddInstanceTags(vm.backend_vm_id, [tag],
851 27435d42 Christos Stavrakakis
                                   dry_run=settings.TEST)
852 9afeb669 Kostas Papadimitriou
853 3524241a Christos Stavrakakis
        # XXX NOP ModifyInstance call to force process_net_status to run
854 3524241a Christos Stavrakakis
        # on the dispatcher
855 cc92b70f Christos Stavrakakis
        os_name = settings.GANETI_CREATEINSTANCE_KWARGS['os']
856 3524241a Christos Stavrakakis
        client.ModifyInstance(vm.backend_vm_id,
857 cc92b70f Christos Stavrakakis
                              os_name=os_name)
858 41a7fae7 Christos Stavrakakis
    return None
859 5eedb0e4 Vangelis Koukis
860 41303ed0 Vangelis Koukis
861 e77a29ab Christos Stavrakakis
def get_instances(backend, bulk=True):
862 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
863 e77a29ab Christos Stavrakakis
        return c.GetInstances(bulk=bulk)
864 d986cb32 Christos Stavrakakis
865 f5b4f2a3 Christos Stavrakakis
866 e77a29ab Christos Stavrakakis
def get_nodes(backend, bulk=True):
867 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
868 e77a29ab Christos Stavrakakis
        return c.GetNodes(bulk=bulk)
869 1a894bfe Christos Stavrakakis
870 1a894bfe Christos Stavrakakis
871 70a0afab Christos Stavrakakis
def get_jobs(backend, bulk=True):
872 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
873 70a0afab Christos Stavrakakis
        return c.GetJobs(bulk=bulk)
874 f5b4f2a3 Christos Stavrakakis
875 17852fe9 Giorgos Verigakis
876 1a894bfe Christos Stavrakakis
def get_physical_resources(backend):
877 1a894bfe Christos Stavrakakis
    """ Get the physical resources of a backend.
878 1a894bfe Christos Stavrakakis

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

881 1a894bfe Christos Stavrakakis
    """
882 e77a29ab Christos Stavrakakis
    nodes = get_nodes(backend, bulk=True)
883 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
884 1a894bfe Christos Stavrakakis
    res = {}
885 1a894bfe Christos Stavrakakis
    for a in attr:
886 1a894bfe Christos Stavrakakis
        res[a] = 0
887 1a894bfe Christos Stavrakakis
    for n in nodes:
888 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
889 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
890 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
891 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
892 1a894bfe Christos Stavrakakis
            for a in attr:
893 1a894bfe Christos Stavrakakis
                res[a] += int(n[a])
894 1a894bfe Christos Stavrakakis
    return res
895 1a894bfe Christos Stavrakakis
896 1a894bfe Christos Stavrakakis
897 1da50fe3 Christos Stavrakakis
def update_backend_resources(backend, resources=None):
898 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
899 1a894bfe Christos Stavrakakis

900 1a894bfe Christos Stavrakakis
    """
901 17852fe9 Giorgos Verigakis
902 1a894bfe Christos Stavrakakis
    if not resources:
903 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
904 41303ed0 Vangelis Koukis
905 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
906 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
907 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
908 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
909 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
910 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
911 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
912 1a894bfe Christos Stavrakakis
    backend.save()
913 1a894bfe Christos Stavrakakis
914 1a894bfe Christos Stavrakakis
915 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
916 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
917 1a894bfe Christos Stavrakakis

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

921 1a894bfe Christos Stavrakakis
    """
922 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
923 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
924 1a894bfe Christos Stavrakakis
    mem = 0
925 1a894bfe Christos Stavrakakis
    for i in instances:
926 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
927 1a894bfe Christos Stavrakakis
    return mem
928 b3d28af2 Christos Stavrakakis
929 1da50fe3 Christos Stavrakakis
930 1da50fe3 Christos Stavrakakis
def get_available_disk_templates(backend):
931 1da50fe3 Christos Stavrakakis
    """Get the list of available disk templates of a Ganeti backend.
932 1da50fe3 Christos Stavrakakis

933 1da50fe3 Christos Stavrakakis
    The list contains the disk templates that are enabled in the Ganeti backend
934 1da50fe3 Christos Stavrakakis
    and also included in ipolicy-disk-templates.
935 1da50fe3 Christos Stavrakakis

936 1da50fe3 Christos Stavrakakis
    """
937 1da50fe3 Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
938 1da50fe3 Christos Stavrakakis
        info = c.GetInfo()
939 1da50fe3 Christos Stavrakakis
    enabled_disk_templates = info["enabled_disk_templates"]
940 1da50fe3 Christos Stavrakakis
    ipolicy_disk_templates = info["ipolicy"]["disk-templates"]
941 1da50fe3 Christos Stavrakakis
    return [dp for dp in enabled_disk_templates
942 1da50fe3 Christos Stavrakakis
            if dp in ipolicy_disk_templates]
943 1da50fe3 Christos Stavrakakis
944 1da50fe3 Christos Stavrakakis
945 1da50fe3 Christos Stavrakakis
def update_backend_disk_templates(backend):
946 1da50fe3 Christos Stavrakakis
    disk_templates = get_available_disk_templates(backend)
947 1da50fe3 Christos Stavrakakis
    backend.disk_templates = disk_templates
948 1da50fe3 Christos Stavrakakis
    backend.save()
949 1da50fe3 Christos Stavrakakis
950 1da50fe3 Christos Stavrakakis
951 b3d28af2 Christos Stavrakakis
##
952 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
953 b3d28af2 Christos Stavrakakis
##
954 b3d28af2 Christos Stavrakakis
955 b3d28af2 Christos Stavrakakis
956 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
957 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
958 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
959 b3d28af2 Christos Stavrakakis
        return result
960 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
961 b3d28af2 Christos Stavrakakis
    return result
962 b3d28af2 Christos Stavrakakis
963 b3d28af2 Christos Stavrakakis
964 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
965 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
966 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
967 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
968 3524241a Christos Stavrakakis
    return result
969 b3d28af2 Christos Stavrakakis
970 b3d28af2 Christos Stavrakakis
971 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
972 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
973 3524241a Christos Stavrakakis
        for group in client.GetGroups():
974 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
975 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
976 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
977 3524241a Christos Stavrakakis
            if result[0] != 'success':
978 3524241a Christos Stavrakakis
                return result
979 b3d28af2 Christos Stavrakakis
980 b3d28af2 Christos Stavrakakis
    return result
981 b3d28af2 Christos Stavrakakis
982 b3d28af2 Christos Stavrakakis
983 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
984 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
985 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
986 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
987 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
988 cc92b70f Christos Stavrakakis
                                         [result], None)
989 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
990 b3d28af2 Christos Stavrakakis
991 b3d28af2 Christos Stavrakakis
    if status == 'success':
992 b3d28af2 Christos Stavrakakis
        return (status, None)
993 b3d28af2 Christos Stavrakakis
    else:
994 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
995 b3d28af2 Christos Stavrakakis
        return (status, error)