Statistics
| Branch: | Tag: | Revision:

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

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

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

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

213 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
214 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
215 ad2d6807 Vangelis Koukis

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

357 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
358 99af08a4 Christos Stavrakakis
    backends that network exists.
359 99af08a4 Christos Stavrakakis

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

364 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
365 99af08a4 Christos Stavrakakis
    quotas for the network.
366 99af08a4 Christos Stavrakakis

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

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

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

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

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

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

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

918 1a894bfe Christos Stavrakakis
    """
919 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
920 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
921 1a894bfe Christos Stavrakakis
    mem = 0
922 1a894bfe Christos Stavrakakis
    for i in instances:
923 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
924 1a894bfe Christos Stavrakakis
    return mem
925 b3d28af2 Christos Stavrakakis
926 b3d28af2 Christos Stavrakakis
##
927 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
928 b3d28af2 Christos Stavrakakis
##
929 b3d28af2 Christos Stavrakakis
930 b3d28af2 Christos Stavrakakis
931 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
932 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
933 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
934 b3d28af2 Christos Stavrakakis
        return result
935 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
936 b3d28af2 Christos Stavrakakis
    return result
937 b3d28af2 Christos Stavrakakis
938 b3d28af2 Christos Stavrakakis
939 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
940 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
941 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
942 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
943 3524241a Christos Stavrakakis
    return result
944 b3d28af2 Christos Stavrakakis
945 b3d28af2 Christos Stavrakakis
946 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
947 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
948 3524241a Christos Stavrakakis
        for group in client.GetGroups():
949 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
950 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
951 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
952 3524241a Christos Stavrakakis
            if result[0] != 'success':
953 3524241a Christos Stavrakakis
                return result
954 b3d28af2 Christos Stavrakakis
955 b3d28af2 Christos Stavrakakis
    return result
956 b3d28af2 Christos Stavrakakis
957 b3d28af2 Christos Stavrakakis
958 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
959 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
960 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
961 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
962 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
963 cc92b70f Christos Stavrakakis
                                         [result], None)
964 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
965 b3d28af2 Christos Stavrakakis
966 b3d28af2 Christos Stavrakakis
    if status == 'success':
967 b3d28af2 Christos Stavrakakis
        return (status, None)
968 b3d28af2 Christos Stavrakakis
    else:
969 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
970 b3d28af2 Christos Stavrakakis
        return (status, error)