Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (47.7 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 a1baa42b Christos Stavrakakis
from datetime import datetime, timedelta
36 207b70d5 Giorgos Verigakis
37 5f90e24c Christos Stavrakakis
from synnefo.db.models import (VirtualMachine, Network,
38 3524241a Christos Stavrakakis
                               BackendNetwork, BACKEND_STATUSES,
39 ca4d59e3 Christos Stavrakakis
                               pooled_rapi_client, VirtualMachineDiagnostic,
40 bfb3f9c2 Christos Stavrakakis
                               Flavor, IPAddress, IPAddressLog)
41 0292883e Christos Stavrakakis
from synnefo.logic import utils, ips
42 cb4eee84 Christos Stavrakakis
from synnefo import quotas
43 b7d38981 Dimitris Aragiorgis
from synnefo.api.util import release_resource
44 fd95834e Christos Stavrakakis
from synnefo.util.mac2eui64 import mac2eui64
45 d2036274 Christos Stavrakakis
from synnefo.logic import rapi
46 529178b1 Giorgos Verigakis
47 3524241a Christos Stavrakakis
from logging import getLogger
48 3524241a Christos Stavrakakis
log = getLogger(__name__)
49 9e98ba3c Giorgos Verigakis
50 529178b1 Giorgos Verigakis
51 efff6193 Giorgos Verigakis
_firewall_tags = {
52 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
53 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
54 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
55 efff6193 Giorgos Verigakis
56 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
57 efff6193 Giorgos Verigakis
58 3278725f Christos Stavrakakis
SIMPLE_NIC_FIELDS = ["state", "mac", "network", "firewall_profile", "index"]
59 3278725f Christos Stavrakakis
COMPLEX_NIC_FIELDS = ["ipv4_address", "ipv6_address"]
60 3278725f Christos Stavrakakis
NIC_FIELDS = SIMPLE_NIC_FIELDS + COMPLEX_NIC_FIELDS
61 c583d487 Christos Stavrakakis
DISK_FIELDS = ["status", "size", "index"]
62 0d069390 Christos Stavrakakis
UNKNOWN_NIC_PREFIX = "unknown-"
63 c583d487 Christos Stavrakakis
UNKNOWN_DISK_PREFIX = "unknown-"
64 a1baa42b Christos Stavrakakis
65 02feca11 Vassilios Karakoidas
66 41a7fae7 Christos Stavrakakis
def handle_vm_quotas(vm, job_id, job_opcode, job_status, job_fields):
67 41a7fae7 Christos Stavrakakis
    """Handle quotas for updated VirtualMachine.
68 41a7fae7 Christos Stavrakakis

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

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

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

131 02feca11 Vassilios Karakoidas
    """
132 41303ed0 Vangelis Koukis
    # See #1492, #1031, #1111 why this line has been removed
133 41303ed0 Vangelis Koukis
    #if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
134 fd65ab41 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
135 02feca11 Vassilios Karakoidas
        raise VirtualMachine.InvalidBackendMsgError(opcode, status)
136 02feca11 Vassilios Karakoidas
137 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = jobid
138 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = status
139 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = opcode
140 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = logmsg
141 02feca11 Vassilios Karakoidas
142 d2036274 Christos Stavrakakis
    if status not in rapi.JOB_STATUS_FINALIZED:
143 41a7fae7 Christos Stavrakakis
        vm.save()
144 41a7fae7 Christos Stavrakakis
        return
145 41a7fae7 Christos Stavrakakis
146 e6fbada1 Christos Stavrakakis
    if job_fields is None:
147 e6fbada1 Christos Stavrakakis
        job_fields = {}
148 1fdd8d69 Christos Stavrakakis
149 1fdd8d69 Christos Stavrakakis
    new_operstate = None
150 3af1fb4b Christos Stavrakakis
    new_flavor = None
151 41a7fae7 Christos Stavrakakis
    state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode)
152 32a0b855 Giorgos Korfiatis
153 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS:
154 1fdd8d69 Christos Stavrakakis
        # If job succeeds, change operating state if needed
155 41a7fae7 Christos Stavrakakis
        if state_for_success is not None:
156 1fdd8d69 Christos Stavrakakis
            new_operstate = state_for_success
157 1fdd8d69 Christos Stavrakakis
158 e6fbada1 Christos Stavrakakis
        beparams = job_fields.get("beparams", None)
159 41a7fae7 Christos Stavrakakis
        if beparams:
160 41a7fae7 Christos Stavrakakis
            # Change the flavor of the VM
161 3af1fb4b Christos Stavrakakis
            new_flavor = _process_resize(vm, beparams)
162 1fdd8d69 Christos Stavrakakis
163 41a7fae7 Christos Stavrakakis
        # Update backendtime only for jobs that have been successfully
164 41a7fae7 Christos Stavrakakis
        # completed, since only these jobs update the state of the VM. Else a
165 41a7fae7 Christos Stavrakakis
        # "race condition" may occur when a successful job (e.g.
166 41a7fae7 Christos Stavrakakis
        # OP_INSTANCE_REMOVE) completes before an error job and messages arrive
167 41a7fae7 Christos Stavrakakis
        # in reversed order.
168 41a7fae7 Christos Stavrakakis
        vm.backendtime = etime
169 ca4d59e3 Christos Stavrakakis
170 c583d487 Christos Stavrakakis
    if status in rapi.JOB_STATUS_FINALIZED:
171 c583d487 Christos Stavrakakis
        if nics is not None:  # Update the NICs of the VM
172 c583d487 Christos Stavrakakis
            _process_net_status(vm, etime, nics)
173 c583d487 Christos Stavrakakis
        if disks is not None:  # Update the disks of the VM
174 c583d487 Christos Stavrakakis
            _process_disks_status(vm, etime, disks)
175 90858bda Christos Stavrakakis
176 91954b45 Christos Stavrakakis
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
177 d2036274 Christos Stavrakakis
    if opcode == 'OP_INSTANCE_CREATE' and status in (rapi.JOB_STATUS_CANCELED,
178 d2036274 Christos Stavrakakis
                                                     rapi.JOB_STATUS_ERROR):
179 1fdd8d69 Christos Stavrakakis
        new_operstate = "ERROR"
180 c4ce868e Christos Stavrakakis
        vm.backendtime = etime
181 c583d487 Christos Stavrakakis
        # Update state of associated attachments
182 96feddae Christos Stavrakakis
        vm.nics.all().update(state="ERROR")
183 c583d487 Christos Stavrakakis
        vm.volumes.all().update(status="ERROR")
184 cb4eee84 Christos Stavrakakis
    elif opcode == 'OP_INSTANCE_REMOVE':
185 e97288bc Christos Stavrakakis
        # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR,
186 e97288bc Christos Stavrakakis
        # when no instance exists at the Ganeti backend.
187 e97288bc Christos Stavrakakis
        # See ticket #799 for all the details.
188 d2036274 Christos Stavrakakis
        if (status == rapi.JOB_STATUS_SUCCESS or
189 d2036274 Christos Stavrakakis
           (status == rapi.JOB_STATUS_ERROR and not vm_exists_in_backend(vm))):
190 c583d487 Christos Stavrakakis
            # server has been deleted, so delete the server's attachments
191 c583d487 Christos Stavrakakis
            vm.volumes.all().update(deleted=True, machine=None)
192 a1baa42b Christos Stavrakakis
            for nic in vm.nics.all():
193 c583d487 Christos Stavrakakis
                # but first release the IP
194 3278725f Christos Stavrakakis
                remove_nic_ips(nic)
195 a1baa42b Christos Stavrakakis
                nic.delete()
196 e97288bc Christos Stavrakakis
            vm.deleted = True
197 1fdd8d69 Christos Stavrakakis
            new_operstate = state_for_success
198 e97288bc Christos Stavrakakis
            vm.backendtime = etime
199 d2036274 Christos Stavrakakis
            status = rapi.JOB_STATUS_SUCCESS
200 41a7fae7 Christos Stavrakakis
201 d2036274 Christos Stavrakakis
    if status in rapi.JOB_STATUS_FINALIZED:
202 41a7fae7 Christos Stavrakakis
        # Job is finalized: Handle quotas/commissioning
203 41a7fae7 Christos Stavrakakis
        vm = handle_vm_quotas(vm, job_id=jobid, job_opcode=opcode,
204 41a7fae7 Christos Stavrakakis
                              job_status=status, job_fields=job_fields)
205 41a7fae7 Christos Stavrakakis
        # and clear task fields
206 41a7fae7 Christos Stavrakakis
        if vm.task_job_id == jobid:
207 41a7fae7 Christos Stavrakakis
            vm.task = None
208 41a7fae7 Christos Stavrakakis
            vm.task_job_id = None
209 02feca11 Vassilios Karakoidas
210 1fdd8d69 Christos Stavrakakis
    if new_operstate is not None:
211 1fdd8d69 Christos Stavrakakis
        vm.operstate = new_operstate
212 3af1fb4b Christos Stavrakakis
    if new_flavor is not None:
213 3af1fb4b Christos Stavrakakis
        vm.flavor = new_flavor
214 1fdd8d69 Christos Stavrakakis
215 02feca11 Vassilios Karakoidas
    vm.save()
216 22e52ede Vassilios Karakoidas
217 ad2d6807 Vangelis Koukis
218 ca4d59e3 Christos Stavrakakis
def _process_resize(vm, beparams):
219 ca4d59e3 Christos Stavrakakis
    """Change flavor of a VirtualMachine based on new beparams."""
220 ca4d59e3 Christos Stavrakakis
    old_flavor = vm.flavor
221 41a7fae7 Christos Stavrakakis
    vcpus = beparams.get("vcpus", old_flavor.cpu)
222 41a7fae7 Christos Stavrakakis
    ram = beparams.get("maxmem", old_flavor.ram)
223 41a7fae7 Christos Stavrakakis
    if vcpus == old_flavor.cpu and ram == old_flavor.ram:
224 ca4d59e3 Christos Stavrakakis
        return
225 ca4d59e3 Christos Stavrakakis
    try:
226 ca4d59e3 Christos Stavrakakis
        new_flavor = Flavor.objects.get(cpu=vcpus, ram=ram,
227 ca4d59e3 Christos Stavrakakis
                                        disk=old_flavor.disk,
228 ca4d59e3 Christos Stavrakakis
                                        disk_template=old_flavor.disk_template)
229 ca4d59e3 Christos Stavrakakis
    except Flavor.DoesNotExist:
230 8d5795b4 Christos Stavrakakis
        raise Exception("Cannot find flavor for VM")
231 3af1fb4b Christos Stavrakakis
    return new_flavor
232 ca4d59e3 Christos Stavrakakis
233 ca4d59e3 Christos Stavrakakis
234 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
235 c4e55622 Christos Stavrakakis
def process_net_status(vm, etime, nics):
236 fd95834e Christos Stavrakakis
    """Wrap _process_net_status inside transaction."""
237 fd95834e Christos Stavrakakis
    _process_net_status(vm, etime, nics)
238 fd95834e Christos Stavrakakis
239 fd95834e Christos Stavrakakis
240 fd95834e Christos Stavrakakis
def _process_net_status(vm, etime, nics):
241 ad2d6807 Vangelis Koukis
    """Process a net status notification from the backend
242 ad2d6807 Vangelis Koukis

243 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
244 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
245 ad2d6807 Vangelis Koukis

246 ad2d6807 Vangelis Koukis
    Update the state of the VM in the DB accordingly.
247 37ca953f Christodoulos Psaltis

248 a1baa42b Christos Stavrakakis
    """
249 b578d9e7 Christos Stavrakakis
    ganeti_nics = process_ganeti_nics(nics)
250 3278725f Christos Stavrakakis
    db_nics = dict([(nic.id, nic)
251 40576cf5 Christos Stavrakakis
                    for nic in vm.nics.select_related("network")
252 40576cf5 Christos Stavrakakis
                                      .prefetch_related("ips")])
253 40d53b77 Christos Stavrakakis
254 a1baa42b Christos Stavrakakis
    for nic_name in set(db_nics.keys()) | set(ganeti_nics.keys()):
255 a1baa42b Christos Stavrakakis
        db_nic = db_nics.get(nic_name)
256 a1baa42b Christos Stavrakakis
        ganeti_nic = ganeti_nics.get(nic_name)
257 a1baa42b Christos Stavrakakis
        if ganeti_nic is None:
258 1cb7846c Christos Stavrakakis
            if nic_is_stale(vm, nic):
259 1cb7846c Christos Stavrakakis
                log.debug("Removing stale NIC '%s'" % db_nic)
260 3278725f Christos Stavrakakis
                remove_nic_ips(db_nic)
261 0d069390 Christos Stavrakakis
                db_nic.delete()
262 a1baa42b Christos Stavrakakis
            else:
263 1cb7846c Christos Stavrakakis
                log.info("NIC '%s' is still being created" % db_nic)
264 a1baa42b Christos Stavrakakis
        elif db_nic is None:
265 3278725f Christos Stavrakakis
            msg = ("NIC/%s of VM %s does not exist in DB! Cannot automatically"
266 3278725f Christos Stavrakakis
                   " fix this issue!" % (nic_name, vm))
267 3278725f Christos Stavrakakis
            log.error(msg)
268 3278725f Christos Stavrakakis
            continue
269 a1baa42b Christos Stavrakakis
        elif not nics_are_equal(db_nic, ganeti_nic):
270 3278725f Christos Stavrakakis
            for f in SIMPLE_NIC_FIELDS:
271 3278725f Christos Stavrakakis
                # Update the NIC in DB with the values from Ganeti NIC
272 3278725f Christos Stavrakakis
                setattr(db_nic, f, ganeti_nic[f])
273 3278725f Christos Stavrakakis
                db_nic.save()
274 bfb3f9c2 Christos Stavrakakis
275 a1baa42b Christos Stavrakakis
            # Special case where the IPv4 address has changed, because you
276 a1baa42b Christos Stavrakakis
            # need to release the old IPv4 address and reserve the new one
277 40576cf5 Christos Stavrakakis
            gnt_ipv4_address = ganeti_nic["ipv4_address"]
278 40576cf5 Christos Stavrakakis
            db_ipv4_address = db_nic.ipv4_address
279 40576cf5 Christos Stavrakakis
            if db_ipv4_address != gnt_ipv4_address:
280 bfb3f9c2 Christos Stavrakakis
                change_address_of_port(db_nic, vm.userid,
281 40576cf5 Christos Stavrakakis
                                       old_address=db_ipv4_address,
282 40576cf5 Christos Stavrakakis
                                       new_address=gnt_ipv4_address,
283 bfb3f9c2 Christos Stavrakakis
                                       version=4)
284 bfb3f9c2 Christos Stavrakakis
285 40576cf5 Christos Stavrakakis
            gnt_ipv6_address = ganeti_nic["ipv6_address"]
286 40576cf5 Christos Stavrakakis
            db_ipv6_address = db_nic.ipv6_address
287 40576cf5 Christos Stavrakakis
            if db_ipv6_address != gnt_ipv6_address:
288 bfb3f9c2 Christos Stavrakakis
                change_address_of_port(db_nic, vm.userid,
289 40576cf5 Christos Stavrakakis
                                       old_address=db_ipv6_address,
290 40576cf5 Christos Stavrakakis
                                       new_address=gnt_ipv6_address,
291 bfb3f9c2 Christos Stavrakakis
                                       version=6)
292 b578d9e7 Christos Stavrakakis
293 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
294 b578d9e7 Christos Stavrakakis
    vm.save()
295 b578d9e7 Christos Stavrakakis
296 b578d9e7 Christos Stavrakakis
297 bfb3f9c2 Christos Stavrakakis
def change_address_of_port(port, userid, old_address, new_address, version):
298 bfb3f9c2 Christos Stavrakakis
    """Change."""
299 bfb3f9c2 Christos Stavrakakis
    if old_address is not None:
300 bfb3f9c2 Christos Stavrakakis
        msg = ("IPv%s Address of server '%s' changed from '%s' to '%s'"
301 bfb3f9c2 Christos Stavrakakis
               % (version, port.machine_id, old_address, new_address))
302 57374655 Christos Stavrakakis
        log.error(msg)
303 bfb3f9c2 Christos Stavrakakis
304 bfb3f9c2 Christos Stavrakakis
    # Remove the old IP address
305 bfb3f9c2 Christos Stavrakakis
    remove_nic_ips(port, version=version)
306 bfb3f9c2 Christos Stavrakakis
307 bfb3f9c2 Christos Stavrakakis
    if version == 4:
308 bfb3f9c2 Christos Stavrakakis
        ipaddress = ips.allocate_ip(port.network, userid, address=new_address)
309 bfb3f9c2 Christos Stavrakakis
        ipaddress.nic = port
310 bfb3f9c2 Christos Stavrakakis
        ipaddress.save()
311 bfb3f9c2 Christos Stavrakakis
    elif version == 6:
312 bfb3f9c2 Christos Stavrakakis
        subnet6 = port.network.subnet6
313 bfb3f9c2 Christos Stavrakakis
        ipaddress = IPAddress.objects.create(userid=userid,
314 bfb3f9c2 Christos Stavrakakis
                                             network=port.network,
315 bfb3f9c2 Christos Stavrakakis
                                             subnet=subnet6,
316 bfb3f9c2 Christos Stavrakakis
                                             nic=port,
317 5920f82c Christos Stavrakakis
                                             address=new_address,
318 5920f82c Christos Stavrakakis
                                             ipversion=6)
319 bfb3f9c2 Christos Stavrakakis
    else:
320 bfb3f9c2 Christos Stavrakakis
        raise ValueError("Unknown version: %s" % version)
321 bfb3f9c2 Christos Stavrakakis
322 bfb3f9c2 Christos Stavrakakis
    # New address log
323 bfb3f9c2 Christos Stavrakakis
    ip_log = IPAddressLog.objects.create(server_id=port.machine_id,
324 bfb3f9c2 Christos Stavrakakis
                                         network_id=port.network_id,
325 bfb3f9c2 Christos Stavrakakis
                                         address=new_address,
326 bfb3f9c2 Christos Stavrakakis
                                         active=True)
327 bfb3f9c2 Christos Stavrakakis
    log.info("Created IP log entry '%s' for address '%s' to server '%s'",
328 bfb3f9c2 Christos Stavrakakis
             ip_log.id, new_address, port.machine_id)
329 bfb3f9c2 Christos Stavrakakis
330 bfb3f9c2 Christos Stavrakakis
    return ipaddress
331 bfb3f9c2 Christos Stavrakakis
332 bfb3f9c2 Christos Stavrakakis
333 a1baa42b Christos Stavrakakis
def nics_are_equal(db_nic, gnt_nic):
334 a1baa42b Christos Stavrakakis
    for field in NIC_FIELDS:
335 a1baa42b Christos Stavrakakis
        if getattr(db_nic, field) != gnt_nic[field]:
336 a1baa42b Christos Stavrakakis
            return False
337 a1baa42b Christos Stavrakakis
    return True
338 a1baa42b Christos Stavrakakis
339 a1baa42b Christos Stavrakakis
340 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
341 a1baa42b Christos Stavrakakis
    """Process NIC dict from ganeti"""
342 b578d9e7 Christos Stavrakakis
    new_nics = []
343 a1baa42b Christos Stavrakakis
    for index, gnic in enumerate(ganeti_nics):
344 a1baa42b Christos Stavrakakis
        nic_name = gnic.get("name", None)
345 a1baa42b Christos Stavrakakis
        if nic_name is not None:
346 a1baa42b Christos Stavrakakis
            nic_id = utils.id_from_nic_name(nic_name)
347 a1baa42b Christos Stavrakakis
        else:
348 a1baa42b Christos Stavrakakis
            # Put as default value the index. If it is an unknown NIC to
349 a1baa42b Christos Stavrakakis
            # synnefo it will be created automaticaly.
350 0d069390 Christos Stavrakakis
            nic_id = UNKNOWN_NIC_PREFIX + str(index)
351 a1baa42b Christos Stavrakakis
        network_name = gnic.get('network', '')
352 a1baa42b Christos Stavrakakis
        network_id = utils.id_from_network_name(network_name)
353 a1baa42b Christos Stavrakakis
        network = Network.objects.get(id=network_id)
354 77f0fa63 Christos Stavrakakis
355 77f0fa63 Christos Stavrakakis
        # Get the new nic info
356 a1baa42b Christos Stavrakakis
        mac = gnic.get('mac')
357 a1baa42b Christos Stavrakakis
        ipv4 = gnic.get('ip')
358 8764d304 Christos Stavrakakis
        subnet6 = network.subnet6
359 bfb3f9c2 Christos Stavrakakis
        ipv6 = mac2eui64(mac, subnet6.cidr) if subnet6 else None
360 8d325d4b Christos Stavrakakis
361 a1baa42b Christos Stavrakakis
        firewall = gnic.get('firewall')
362 8d325d4b Christos Stavrakakis
        firewall_profile = _reverse_tags.get(firewall)
363 a1baa42b Christos Stavrakakis
        if not firewall_profile and network.public:
364 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
365 9afeb669 Kostas Papadimitriou
366 a1baa42b Christos Stavrakakis
        nic_info = {
367 a1baa42b Christos Stavrakakis
            'index': index,
368 a1baa42b Christos Stavrakakis
            'network': network,
369 cc92b70f Christos Stavrakakis
            'mac': mac,
370 8764d304 Christos Stavrakakis
            'ipv4_address': ipv4,
371 8764d304 Christos Stavrakakis
            'ipv6_address': ipv6,
372 939d71dd Christos Stavrakakis
            'firewall_profile': firewall_profile,
373 939d71dd Christos Stavrakakis
            'state': 'ACTIVE'}
374 b578d9e7 Christos Stavrakakis
375 a1baa42b Christos Stavrakakis
        new_nics.append((nic_id, nic_info))
376 a1baa42b Christos Stavrakakis
    return dict(new_nics)
377 b578d9e7 Christos Stavrakakis
378 b578d9e7 Christos Stavrakakis
379 bfb3f9c2 Christos Stavrakakis
def remove_nic_ips(nic, version=None):
380 3278725f Christos Stavrakakis
    """Remove IP addresses associated with a NetworkInterface.
381 a1baa42b Christos Stavrakakis

382 3278725f Christos Stavrakakis
    Remove all IP addresses that are associated with the NetworkInterface
383 3278725f Christos Stavrakakis
    object, by returning them to the pool and deleting the IPAddress object. If
384 3278725f Christos Stavrakakis
    the IP is a floating IP, then it is just disassociated from the NIC.
385 bfb3f9c2 Christos Stavrakakis
    If version is specified, then only IP addressses of that version will be
386 bfb3f9c2 Christos Stavrakakis
    removed.
387 a1baa42b Christos Stavrakakis

388 a1baa42b Christos Stavrakakis
    """
389 8764d304 Christos Stavrakakis
    for ip in nic.ips.all():
390 bfb3f9c2 Christos Stavrakakis
        if version and ip.ipversion != version:
391 bfb3f9c2 Christos Stavrakakis
            continue
392 bfb3f9c2 Christos Stavrakakis
393 ff863a80 Christos Stavrakakis
        # Update the DB table holding the logging of all IP addresses
394 bfb3f9c2 Christos Stavrakakis
        terminate_active_ipaddress_log(nic, ip)
395 ff863a80 Christos Stavrakakis
396 bfb3f9c2 Christos Stavrakakis
        if ip.floating_ip:
397 bfb3f9c2 Christos Stavrakakis
            ip.nic = None
398 bfb3f9c2 Christos Stavrakakis
            ip.save()
399 bfb3f9c2 Christos Stavrakakis
        else:
400 bfb3f9c2 Christos Stavrakakis
            # Release the IPv4 address
401 bfb3f9c2 Christos Stavrakakis
            ip.release_address()
402 8764d304 Christos Stavrakakis
            ip.delete()
403 bd392934 Christos Stavrakakis
404 bd392934 Christos Stavrakakis
405 bfb3f9c2 Christos Stavrakakis
def terminate_active_ipaddress_log(nic, ip):
406 ff863a80 Christos Stavrakakis
    """Update DB logging entry for this IP address."""
407 fae6e5f0 Christos Stavrakakis
    if not ip.network.public or nic.machine is None:
408 ff863a80 Christos Stavrakakis
        return
409 ff863a80 Christos Stavrakakis
    try:
410 ff863a80 Christos Stavrakakis
        ip_log, created = \
411 ff863a80 Christos Stavrakakis
            IPAddressLog.objects.get_or_create(server_id=nic.machine_id,
412 ff863a80 Christos Stavrakakis
                                               network_id=ip.network_id,
413 ff863a80 Christos Stavrakakis
                                               address=ip.address,
414 ff863a80 Christos Stavrakakis
                                               active=True)
415 ff863a80 Christos Stavrakakis
    except IPAddressLog.MultipleObjectsReturned:
416 ff863a80 Christos Stavrakakis
        logmsg = ("Multiple active log entries for IP %s, Network %s,"
417 8d5795b4 Christos Stavrakakis
                  "Server %s. Cannot proceed!"
418 ff863a80 Christos Stavrakakis
                  % (ip.address, ip.network, nic.machine))
419 ff863a80 Christos Stavrakakis
        log.error(logmsg)
420 ff863a80 Christos Stavrakakis
        raise
421 ff863a80 Christos Stavrakakis
422 ff863a80 Christos Stavrakakis
    if created:
423 ff863a80 Christos Stavrakakis
        logmsg = ("No log entry for IP %s, Network %s, Server %s. Created new"
424 ff863a80 Christos Stavrakakis
                  " but with wrong creation timestamp."
425 ff863a80 Christos Stavrakakis
                  % (ip.address, ip.network, nic.machine))
426 ff863a80 Christos Stavrakakis
        log.error(logmsg)
427 ff863a80 Christos Stavrakakis
    ip_log.released_at = datetime.now()
428 ff863a80 Christos Stavrakakis
    ip_log.active = False
429 ff863a80 Christos Stavrakakis
    ip_log.save()
430 bd392934 Christos Stavrakakis
431 bd392934 Christos Stavrakakis
432 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
433 c583d487 Christos Stavrakakis
def process_disks_status(vm, etime, disks):
434 c583d487 Christos Stavrakakis
    """Wrap _process_disks_status inside transaction."""
435 c583d487 Christos Stavrakakis
    _process_disks_status(vm, etime, disks)
436 c583d487 Christos Stavrakakis
437 c583d487 Christos Stavrakakis
438 c583d487 Christos Stavrakakis
def _process_disks_status(vm, etime, disks):
439 c583d487 Christos Stavrakakis
    """Process a disks status notification from the backend
440 c583d487 Christos Stavrakakis

441 c583d487 Christos Stavrakakis
    Process an incoming message from the Ganeti backend,
442 c583d487 Christos Stavrakakis
    detailing the disk configuration of a VM instance.
443 c583d487 Christos Stavrakakis

444 c583d487 Christos Stavrakakis
    Update the state of the VM in the DB accordingly.
445 c583d487 Christos Stavrakakis

446 c583d487 Christos Stavrakakis
    """
447 c583d487 Christos Stavrakakis
    ganeti_disks = process_ganeti_disks(disks)
448 c583d487 Christos Stavrakakis
    db_disks = dict([(disk.id, disk)
449 c583d487 Christos Stavrakakis
                     for disk in vm.volumes.filter(deleted=False)])
450 c583d487 Christos Stavrakakis
451 c583d487 Christos Stavrakakis
    for disk_name in set(db_disks.keys()) | set(ganeti_disks.keys()):
452 c583d487 Christos Stavrakakis
        db_disk = db_disks.get(disk_name)
453 c583d487 Christos Stavrakakis
        ganeti_disk = ganeti_disks.get(disk_name)
454 c583d487 Christos Stavrakakis
        if ganeti_disk is None:
455 c583d487 Christos Stavrakakis
            if disk_is_stale(vm, disk):
456 c583d487 Christos Stavrakakis
                log.debug("Removing stale disk '%s'" % db_disk)
457 c583d487 Christos Stavrakakis
                # TODO: Handle disk deletion
458 c583d487 Christos Stavrakakis
                db_disk.deleted = True
459 c583d487 Christos Stavrakakis
                db_disk.save()
460 c583d487 Christos Stavrakakis
            else:
461 c583d487 Christos Stavrakakis
                log.info("disk '%s' is still being created" % db_disk)
462 c583d487 Christos Stavrakakis
        elif db_disk is None:
463 c583d487 Christos Stavrakakis
            msg = ("disk/%s of VM %s does not exist in DB! Cannot"
464 c583d487 Christos Stavrakakis
                   " automatically fix this issue!" % (disk_name, vm))
465 c583d487 Christos Stavrakakis
            log.error(msg)
466 c583d487 Christos Stavrakakis
            continue
467 c583d487 Christos Stavrakakis
        elif not disks_are_equal(db_disk, ganeti_disk):
468 c583d487 Christos Stavrakakis
            for f in DISK_FIELDS:
469 c583d487 Christos Stavrakakis
                # Update the disk in DB with the values from Ganeti disk
470 c583d487 Christos Stavrakakis
                setattr(db_disk, f, ganeti_disk[f])
471 c583d487 Christos Stavrakakis
                db_disk.save()
472 c583d487 Christos Stavrakakis
473 c583d487 Christos Stavrakakis
            # TODO: Special case where the size of the disk has changed!!
474 c583d487 Christos Stavrakakis
            assert(ganeti_disk["size"] == db_disk.size)
475 c583d487 Christos Stavrakakis
476 c583d487 Christos Stavrakakis
    vm.backendtime = etime
477 c583d487 Christos Stavrakakis
    vm.save()
478 c583d487 Christos Stavrakakis
479 c583d487 Christos Stavrakakis
480 c583d487 Christos Stavrakakis
def disks_are_equal(db_disk, gnt_disk):
481 c583d487 Christos Stavrakakis
    for field in DISK_FIELDS:
482 c583d487 Christos Stavrakakis
        if getattr(db_disk, field) != gnt_disk[field]:
483 c583d487 Christos Stavrakakis
            return False
484 c583d487 Christos Stavrakakis
    return True
485 c583d487 Christos Stavrakakis
486 c583d487 Christos Stavrakakis
487 c583d487 Christos Stavrakakis
def process_ganeti_disks(ganeti_disks):
488 c583d487 Christos Stavrakakis
    """Process disk dict from ganeti"""
489 c583d487 Christos Stavrakakis
    new_disks = []
490 c583d487 Christos Stavrakakis
    for index, gdisk in enumerate(ganeti_disks):
491 c583d487 Christos Stavrakakis
        disk_name = gdisk.get("name", None)
492 c583d487 Christos Stavrakakis
        if disk_name is not None:
493 c583d487 Christos Stavrakakis
            disk_id = utils.id_from_disk_name(disk_name)
494 c583d487 Christos Stavrakakis
        else:
495 c583d487 Christos Stavrakakis
            # Put as default value the index. If it is an unknown disk to
496 c583d487 Christos Stavrakakis
            # synnefo it will be created automaticaly.
497 c583d487 Christos Stavrakakis
            disk_id = UNKNOWN_DISK_PREFIX + str(index)
498 c583d487 Christos Stavrakakis
499 c583d487 Christos Stavrakakis
        # Get disk size in GB
500 c583d487 Christos Stavrakakis
        size = gdisk.get("size") >> 10
501 c583d487 Christos Stavrakakis
502 c583d487 Christos Stavrakakis
        disk_info = {
503 c583d487 Christos Stavrakakis
            'index': index,
504 c583d487 Christos Stavrakakis
            'size': size,
505 c583d487 Christos Stavrakakis
            'status': "IN_USE"}
506 c583d487 Christos Stavrakakis
507 c583d487 Christos Stavrakakis
        new_disks.append((disk_id, disk_info))
508 c583d487 Christos Stavrakakis
    return dict(new_disks)
509 c583d487 Christos Stavrakakis
510 c583d487 Christos Stavrakakis
511 c583d487 Christos Stavrakakis
@transaction.commit_on_success
512 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
513 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
514 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
515 22ee6892 Christos Stavrakakis
516 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
517 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
518 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
519 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
520 22ee6892 Christos Stavrakakis
521 c82f57ad Christos Stavrakakis
    # Note: Network is already locked!
522 05146623 Christos Stavrakakis
    network = back_network.network
523 05146623 Christos Stavrakakis
524 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
525 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
526 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS and state_for_success is not None:
527 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
528 22ee6892 Christos Stavrakakis
529 d2036274 Christos Stavrakakis
    if (status in (rapi.JOB_STATUS_CANCELED, rapi.JOB_STATUS_ERROR)
530 d2036274 Christos Stavrakakis
       and opcode == 'OP_NETWORK_ADD'):
531 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
532 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
533 22ee6892 Christos Stavrakakis
534 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
535 d2036274 Christos Stavrakakis
        network_is_deleted = (status == rapi.JOB_STATUS_SUCCESS)
536 d2036274 Christos Stavrakakis
        if network_is_deleted or (status == rapi.JOB_STATUS_ERROR and not
537 198d91c3 Christos Stavrakakis
                                  network_exists_in_backend(back_network)):
538 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
539 e97288bc Christos Stavrakakis
            back_network.deleted = True
540 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
541 22ee6892 Christos Stavrakakis
542 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS:
543 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
544 22ee6892 Christos Stavrakakis
    back_network.save()
545 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
546 05146623 Christos Stavrakakis
    update_network_state(network)
547 fd2bdbb2 Christos Stavrakakis
548 fd2bdbb2 Christos Stavrakakis
549 2509ce17 Christos Stavrakakis
def update_network_state(network):
550 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
551 cb4eee84 Christos Stavrakakis

552 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
553 99af08a4 Christos Stavrakakis
    backends that network exists.
554 99af08a4 Christos Stavrakakis

555 99af08a4 Christos Stavrakakis
    The state of the network is:
556 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
557 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
558 99af08a4 Christos Stavrakakis

559 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
560 99af08a4 Christos Stavrakakis
    quotas for the network.
561 99af08a4 Christos Stavrakakis

562 99af08a4 Christos Stavrakakis
    """
563 99af08a4 Christos Stavrakakis
    if network.deleted:
564 99af08a4 Christos Stavrakakis
        # Network has already been deleted. Just assert that state is also
565 99af08a4 Christos Stavrakakis
        # DELETED
566 99af08a4 Christos Stavrakakis
        if not network.state == "DELETED":
567 99af08a4 Christos Stavrakakis
            network.state = "DELETED"
568 99af08a4 Christos Stavrakakis
            network.save()
569 cb4eee84 Christos Stavrakakis
        return
570 cb4eee84 Christos Stavrakakis
571 99af08a4 Christos Stavrakakis
    backend_states = [s.operstate for s in network.backend_networks.all()]
572 27cda06b Christos Stavrakakis
    if not backend_states and network.action != "DESTROY":
573 99af08a4 Christos Stavrakakis
        if network.state != "ACTIVE":
574 99af08a4 Christos Stavrakakis
            network.state = "ACTIVE"
575 99af08a4 Christos Stavrakakis
            network.save()
576 99af08a4 Christos Stavrakakis
            return
577 99af08a4 Christos Stavrakakis
578 99af08a4 Christos Stavrakakis
    # Network is deleted when all BackendNetworks go to "DELETED" operstate
579 27cda06b Christos Stavrakakis
    deleted = reduce(lambda x, y: x == y and "DELETED", backend_states,
580 27cda06b Christos Stavrakakis
                     "DELETED")
581 99af08a4 Christos Stavrakakis
582 cb4eee84 Christos Stavrakakis
    # Release the resources on the deletion of the Network
583 99af08a4 Christos Stavrakakis
    if deleted:
584 c82f57ad Christos Stavrakakis
        if network.ips.filter(deleted=False, floating_ip=True).exists():
585 8d5795b4 Christos Stavrakakis
            msg = "Cannot delete network %s! Floating IPs still in use!"
586 c82f57ad Christos Stavrakakis
            log.error(msg % network)
587 c82f57ad Christos Stavrakakis
            raise Exception(msg % network)
588 cb4eee84 Christos Stavrakakis
        log.info("Network %r deleted. Releasing link %r mac_prefix %r",
589 cb4eee84 Christos Stavrakakis
                 network.id, network.mac_prefix, network.link)
590 cb4eee84 Christos Stavrakakis
        network.deleted = True
591 99af08a4 Christos Stavrakakis
        network.state = "DELETED"
592 6176e251 Christos Stavrakakis
        # Undrain the network, otherwise the network state will remain
593 6176e251 Christos Stavrakakis
        # as 'SNF:DRAINED'
594 6176e251 Christos Stavrakakis
        network.drained = False
595 b7d38981 Dimitris Aragiorgis
        if network.mac_prefix:
596 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["mac_prefix"] == "pool":
597 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="mac_prefix",
598 b7d38981 Dimitris Aragiorgis
                                 value=network.mac_prefix)
599 b7d38981 Dimitris Aragiorgis
        if network.link:
600 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["link"] == "pool":
601 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="bridge", value=network.link)
602 cb4eee84 Christos Stavrakakis
603 c82f57ad Christos Stavrakakis
        # Set all subnets as deleted
604 c82f57ad Christos Stavrakakis
        network.subnets.update(deleted=True)
605 c82f57ad Christos Stavrakakis
        # And delete the IP pools
606 c82f57ad Christos Stavrakakis
        for subnet in network.subnets.all():
607 c82f57ad Christos Stavrakakis
            if subnet.ipversion == 4:
608 c82f57ad Christos Stavrakakis
                subnet.ip_pools.all().delete()
609 e8234183 Christos Stavrakakis
        # And all the backend networks since there are useless
610 e8234183 Christos Stavrakakis
        network.backend_networks.all().delete()
611 c82f57ad Christos Stavrakakis
612 cb4eee84 Christos Stavrakakis
        # Issue commission
613 e18c1749 Christos Stavrakakis
        if network.userid:
614 368d879e Giorgos Korfiatis
            quotas.issue_and_accept_commission(network, action="DESTROY")
615 32a0b855 Giorgos Korfiatis
            # the above has already saved the object and committed;
616 32a0b855 Giorgos Korfiatis
            # a second save would override others' changes, since the
617 32a0b855 Giorgos Korfiatis
            # object is now unlocked
618 32a0b855 Giorgos Korfiatis
            return
619 e18c1749 Christos Stavrakakis
        elif not network.public:
620 e18c1749 Christos Stavrakakis
            log.warning("Network %s does not have an owner!", network.id)
621 cb4eee84 Christos Stavrakakis
    network.save()
622 cb4eee84 Christos Stavrakakis
623 cb4eee84 Christos Stavrakakis
624 fd2bdbb2 Christos Stavrakakis
@transaction.commit_on_success
625 fd2bdbb2 Christos Stavrakakis
def process_network_modify(back_network, etime, jobid, opcode, status,
626 e6fbada1 Christos Stavrakakis
                           job_fields):
627 fd2bdbb2 Christos Stavrakakis
    assert (opcode == "OP_NETWORK_SET_PARAMS")
628 fd2bdbb2 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
629 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
630 fd2bdbb2 Christos Stavrakakis
631 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobid = jobid
632 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobstatus = status
633 fd2bdbb2 Christos Stavrakakis
    back_network.opcode = opcode
634 fd2bdbb2 Christos Stavrakakis
635 e6fbada1 Christos Stavrakakis
    add_reserved_ips = job_fields.get("add_reserved_ips")
636 fc56ae0f Christos Stavrakakis
    if add_reserved_ips:
637 3278725f Christos Stavrakakis
        network = back_network.network
638 3278725f Christos Stavrakakis
        for ip in add_reserved_ips:
639 3278725f Christos Stavrakakis
            network.reserve_address(ip, external=True)
640 fd2bdbb2 Christos Stavrakakis
641 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS:
642 fd2bdbb2 Christos Stavrakakis
        back_network.backendtime = etime
643 fd2bdbb2 Christos Stavrakakis
    back_network.save()
644 ad2d6807 Vangelis Koukis
645 c25cc9ec Vangelis Koukis
646 9068cd85 Georgios Gousios
@transaction.commit_on_success
647 0827883e Nikos Skalkotos
def process_create_progress(vm, etime, progress):
648 9068cd85 Georgios Gousios
649 0827883e Nikos Skalkotos
    percentage = int(progress)
650 9068cd85 Georgios Gousios
651 af90d919 Vangelis Koukis
    # The percentage may exceed 100%, due to the way
652 0827883e Nikos Skalkotos
    # snf-image:copy-progress tracks bytes read by image handling processes
653 af90d919 Vangelis Koukis
    percentage = 100 if percentage > 100 else percentage
654 af90d919 Vangelis Koukis
    if percentage < 0:
655 af90d919 Vangelis Koukis
        raise ValueError("Percentage cannot be negative")
656 9068cd85 Georgios Gousios
657 7ec9558b Vangelis Koukis
    # FIXME: log a warning here, see #1033
658 7ec9558b Vangelis Koukis
#   if last_update > percentage:
659 7ec9558b Vangelis Koukis
#       raise ValueError("Build percentage should increase monotonically " \
660 7ec9558b Vangelis Koukis
#                        "(old = %d, new = %d)" % (last_update, percentage))
661 9068cd85 Georgios Gousios
662 c25cc9ec Vangelis Koukis
    # This assumes that no message of type 'ganeti-create-progress' is going to
663 c25cc9ec Vangelis Koukis
    # arrive once OP_INSTANCE_CREATE has succeeded for a Ganeti instance and
664 c25cc9ec Vangelis Koukis
    # the instance is STARTED.  What if the two messages are processed by two
665 c25cc9ec Vangelis Koukis
    # separate dispatcher threads, and the 'ganeti-op-status' message for
666 c25cc9ec Vangelis Koukis
    # successful creation gets processed before the 'ganeti-create-progress'
667 c25cc9ec Vangelis Koukis
    # message? [vkoukis]
668 c25cc9ec Vangelis Koukis
    #
669 c25cc9ec Vangelis Koukis
    #if not vm.operstate == 'BUILD':
670 c25cc9ec Vangelis Koukis
    #    raise VirtualMachine.IllegalState("VM is not in building state")
671 9068cd85 Georgios Gousios
672 c25cc9ec Vangelis Koukis
    vm.buildpercentage = percentage
673 c4e55622 Christos Stavrakakis
    vm.backendtime = etime
674 9068cd85 Georgios Gousios
    vm.save()
675 ad2d6807 Vangelis Koukis
676 c25cc9ec Vangelis Koukis
677 952b2a48 Christos Stavrakakis
@transaction.commit_on_success
678 7d43565f Kostas Papadimitriou
def create_instance_diagnostic(vm, message, source, level="DEBUG", etime=None,
679 cc92b70f Christos Stavrakakis
                               details=None):
680 7d43565f Kostas Papadimitriou
    """
681 7d43565f Kostas Papadimitriou
    Create virtual machine instance diagnostic entry.
682 7d43565f Kostas Papadimitriou

683 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
684 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
685 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
686 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
687 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
688 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
689 7d43565f Kostas Papadimitriou
    """
690 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
691 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
692 cc92b70f Christos Stavrakakis
                                                   message=message,
693 cc92b70f Christos Stavrakakis
                                                   details=details)
694 7d43565f Kostas Papadimitriou
695 7d43565f Kostas Papadimitriou
696 e7953d63 Christos Stavrakakis
def create_instance(vm, nics, volumes, flavor, image):
697 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
698 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
699 7f691719 Christos Stavrakakis

700 3a9b3cde Giorgos Verigakis
        metadata value should be a dictionary.
701 3a9b3cde Giorgos Verigakis
    """
702 864bed43 Christos Stavrakakis
703 1c382247 Vangelis Koukis
    # Handle arguments to CreateInstance() as a dictionary,
704 1c382247 Vangelis Koukis
    # initialize it based on a deployment-specific value.
705 1c382247 Vangelis Koukis
    # This enables the administrator to override deployment-specific
706 c25cc9ec Vangelis Koukis
    # arguments, such as the disk template to use, name of os provider
707 1c382247 Vangelis Koukis
    # and hypervisor-specific parameters at will (see Synnefo #785, #835).
708 1c382247 Vangelis Koukis
    #
709 bd87213f Christos Stavrakakis
    kw = vm.backend.get_create_params()
710 1c382247 Vangelis Koukis
    kw['mode'] = 'create'
711 924d8085 Christos Stavrakakis
    kw['name'] = vm.backend_vm_id
712 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
713 296682fe Kostas Papadimitriou
714 5d805533 Christos Stavrakakis
    kw['disk_template'] = volumes[0].template
715 e7953d63 Christos Stavrakakis
    disks = []
716 e7953d63 Christos Stavrakakis
    for volume in volumes:
717 c583d487 Christos Stavrakakis
        disk = {"name": volume.backend_volume_uuid,
718 c583d487 Christos Stavrakakis
                "size": volume.size * 1024}
719 58194535 Christos Stavrakakis
        provider = volume.provider
720 e7953d63 Christos Stavrakakis
        if provider is not None:
721 e7953d63 Christos Stavrakakis
            disk["provider"] = provider
722 5f90e24c Christos Stavrakakis
            disk["origin"] = volume.origin
723 e7953d63 Christos Stavrakakis
            extra_disk_params = settings.GANETI_DISK_PROVIDER_KWARGS\
724 e7953d63 Christos Stavrakakis
                                        .get(provider)
725 e7953d63 Christos Stavrakakis
            if extra_disk_params is not None:
726 e7953d63 Christos Stavrakakis
                disk.update(extra_disk_params)
727 e7953d63 Christos Stavrakakis
        disks.append(disk)
728 e7953d63 Christos Stavrakakis
729 e7953d63 Christos Stavrakakis
    kw["disks"] = disks
730 296682fe Kostas Papadimitriou
731 7c714455 Christos Stavrakakis
    kw['nics'] = [{"name": nic.backend_uuid,
732 7c714455 Christos Stavrakakis
                   "network": nic.network.backend_id,
733 92d2d1ce Christos Stavrakakis
                   "ip": nic.ipv4_address}
734 cb66110b Christos Stavrakakis
                  for nic in nics]
735 d2036274 Christos Stavrakakis
736 cb66110b Christos Stavrakakis
    backend = vm.backend
737 cb66110b Christos Stavrakakis
    depend_jobs = []
738 cb66110b Christos Stavrakakis
    for nic in nics:
739 d2036274 Christos Stavrakakis
        bnet, job_ids = ensure_network_is_active(backend, nic.network_id)
740 d2036274 Christos Stavrakakis
        depend_jobs.extend(job_ids)
741 d2036274 Christos Stavrakakis
742 d2036274 Christos Stavrakakis
    kw["depends"] = create_job_dependencies(depend_jobs)
743 cb66110b Christos Stavrakakis
744 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
745 1c382247 Vangelis Koukis
    # kw['os'] = settings.GANETI_OS_PROVIDER
746 1c382247 Vangelis Koukis
    kw['ip_check'] = False
747 1c382247 Vangelis Koukis
    kw['name_check'] = False
748 79b7dbb7 Christos Stavrakakis
749 1c382247 Vangelis Koukis
    # Do not specific a node explicitly, have
750 1c382247 Vangelis Koukis
    # Ganeti use an iallocator instead
751 f8d1eaf4 Kostas Papadimitriou
    #kw['pnode'] = rapi.GetNodes()[0]
752 79b7dbb7 Christos Stavrakakis
753 1c382247 Vangelis Koukis
    kw['dry_run'] = settings.TEST
754 41303ed0 Vangelis Koukis
755 2b1db26f Giorgos Verigakis
    kw['beparams'] = {
756 cc92b70f Christos Stavrakakis
        'auto_balance': True,
757 cc92b70f Christos Stavrakakis
        'vcpus': flavor.cpu,
758 cc92b70f Christos Stavrakakis
        'memory': flavor.ram}
759 79b7dbb7 Christos Stavrakakis
760 e3b5be49 Giorgos Verigakis
    kw['osparams'] = {
761 79b7dbb7 Christos Stavrakakis
        'config_url': vm.config_url,
762 79b7dbb7 Christos Stavrakakis
        # Store image id and format to Ganeti
763 2a599282 Christos Stavrakakis
        'img_id': image['backend_id'],
764 3a9b3cde Giorgos Verigakis
        'img_format': image['format']}
765 d1eaa651 Christos Stavrakakis
766 e9d59f5e Christos Stavrakakis
    # Use opportunistic locking
767 727fb2f9 Christos Stavrakakis
    kw['opportunistic_locking'] = settings.GANETI_USE_OPPORTUNISTIC_LOCKING
768 e9d59f5e Christos Stavrakakis
769 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
770 1c382247 Vangelis Koukis
    # kw['hvparams'] = dict(serial_console=False)
771 79b7dbb7 Christos Stavrakakis
772 6afeb85d Christos Stavrakakis
    log.debug("Creating instance %s", utils.hide_pass(kw))
773 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
774 3524241a Christos Stavrakakis
        return client.CreateInstance(**kw)
775 f533f224 Vangelis Koukis
776 529178b1 Giorgos Verigakis
777 c397dbce Christos Stavrakakis
def delete_instance(vm, shutdown_timeout=None):
778 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
779 c397dbce Christos Stavrakakis
        return client.DeleteInstance(vm.backend_vm_id,
780 c397dbce Christos Stavrakakis
                                     shutdown_timeout=shutdown_timeout,
781 c397dbce Christos Stavrakakis
                                     dry_run=settings.TEST)
782 529178b1 Giorgos Verigakis
783 ad2d6807 Vangelis Koukis
784 c397dbce Christos Stavrakakis
def reboot_instance(vm, reboot_type, shutdown_timeout=None):
785 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
786 51136096 Christos Stavrakakis
    # Note that reboot type of Ganeti job must be always hard. The 'soft' and
787 51136096 Christos Stavrakakis
    # 'hard' type of OS API is different from the one in Ganeti, and maps to
788 51136096 Christos Stavrakakis
    # 'shutdown_timeout'.
789 bbae3e45 Christos Stavrakakis
    kwargs = {"instance": vm.backend_vm_id,
790 bbae3e45 Christos Stavrakakis
              "reboot_type": "hard"}
791 51136096 Christos Stavrakakis
    # 'shutdown_timeout' parameter is only support from snf-ganeti>=2.8.2 and
792 51136096 Christos Stavrakakis
    # Ganeti > 2.10. In other versions this parameter will be ignored and
793 51136096 Christos Stavrakakis
    # we will fallback to default timeout of Ganeti (120s).
794 c397dbce Christos Stavrakakis
    if shutdown_timeout is not None:
795 c397dbce Christos Stavrakakis
        kwargs["shutdown_timeout"] = shutdown_timeout
796 51136096 Christos Stavrakakis
    if reboot_type == "hard":
797 51136096 Christos Stavrakakis
        kwargs["shutdown_timeout"] = 0
798 bbae3e45 Christos Stavrakakis
    if settings.TEST:
799 bbae3e45 Christos Stavrakakis
        kwargs["dry_run"] = True
800 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
801 bbae3e45 Christos Stavrakakis
        return client.RebootInstance(**kwargs)
802 529178b1 Giorgos Verigakis
803 ad2d6807 Vangelis Koukis
804 529178b1 Giorgos Verigakis
def startup_instance(vm):
805 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
806 3524241a Christos Stavrakakis
        return client.StartupInstance(vm.backend_vm_id, dry_run=settings.TEST)
807 529178b1 Giorgos Verigakis
808 ad2d6807 Vangelis Koukis
809 c397dbce Christos Stavrakakis
def shutdown_instance(vm, shutdown_timeout=None):
810 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
811 c397dbce Christos Stavrakakis
        return client.ShutdownInstance(vm.backend_vm_id,
812 c397dbce Christos Stavrakakis
                                       timeout=shutdown_timeout,
813 c397dbce Christos Stavrakakis
                                       dry_run=settings.TEST)
814 529178b1 Giorgos Verigakis
815 ad2d6807 Vangelis Koukis
816 2cd3f389 Christos Stavrakakis
def resize_instance(vm, vcpus, memory):
817 2cd3f389 Christos Stavrakakis
    beparams = {"vcpus": int(vcpus),
818 2cd3f389 Christos Stavrakakis
                "minmem": int(memory),
819 2cd3f389 Christos Stavrakakis
                "maxmem": int(memory)}
820 2cd3f389 Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
821 2cd3f389 Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, beparams=beparams)
822 2cd3f389 Christos Stavrakakis
823 2cd3f389 Christos Stavrakakis
824 529178b1 Giorgos Verigakis
def get_instance_console(vm):
825 71099804 Vangelis Koukis
    # RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
826 71099804 Vangelis Koukis
    # which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
827 71099804 Vangelis Koukis
    # useless (see #783).
828 71099804 Vangelis Koukis
    #
829 71099804 Vangelis Koukis
    # Until this is fixed on the Ganeti side, construct a console info reply
830 71099804 Vangelis Koukis
    # directly.
831 9afeb669 Kostas Papadimitriou
    #
832 71099804 Vangelis Koukis
    # WARNING: This assumes that VNC runs on port network_port on
833 71099804 Vangelis Koukis
    #          the instance's primary node, and is probably
834 71099804 Vangelis Koukis
    #          hypervisor-specific.
835 71099804 Vangelis Koukis
    #
836 bf5c82dc Christos Stavrakakis
    log.debug("Getting console for vm %s", vm)
837 bf5c82dc Christos Stavrakakis
838 71099804 Vangelis Koukis
    console = {}
839 71099804 Vangelis Koukis
    console['kind'] = 'vnc'
840 3524241a Christos Stavrakakis
841 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
842 3524241a Christos Stavrakakis
        i = client.GetInstance(vm.backend_vm_id)
843 3524241a Christos Stavrakakis
844 bd87213f Christos Stavrakakis
    if vm.backend.hypervisor == "kvm" and i['hvparams']['serial_console']:
845 71099804 Vangelis Koukis
        raise Exception("hv parameter serial_console cannot be true")
846 71099804 Vangelis Koukis
    console['host'] = i['pnode']
847 71099804 Vangelis Koukis
    console['port'] = i['network_port']
848 9afeb669 Kostas Papadimitriou
849 71099804 Vangelis Koukis
    return console
850 604b2bf8 Georgios Gousios
851 604b2bf8 Georgios Gousios
852 3524241a Christos Stavrakakis
def get_instance_info(vm):
853 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
854 198d91c3 Christos Stavrakakis
        return client.GetInstance(vm.backend_vm_id)
855 198d91c3 Christos Stavrakakis
856 198d91c3 Christos Stavrakakis
857 198d91c3 Christos Stavrakakis
def vm_exists_in_backend(vm):
858 198d91c3 Christos Stavrakakis
    try:
859 198d91c3 Christos Stavrakakis
        get_instance_info(vm)
860 198d91c3 Christos Stavrakakis
        return True
861 d2036274 Christos Stavrakakis
    except rapi.GanetiApiError as e:
862 198d91c3 Christos Stavrakakis
        if e.code == 404:
863 198d91c3 Christos Stavrakakis
            return False
864 198d91c3 Christos Stavrakakis
        raise e
865 198d91c3 Christos Stavrakakis
866 198d91c3 Christos Stavrakakis
867 198d91c3 Christos Stavrakakis
def get_network_info(backend_network):
868 198d91c3 Christos Stavrakakis
    with pooled_rapi_client(backend_network) as client:
869 198d91c3 Christos Stavrakakis
        return client.GetNetwork(backend_network.network.backend_id)
870 198d91c3 Christos Stavrakakis
871 198d91c3 Christos Stavrakakis
872 198d91c3 Christos Stavrakakis
def network_exists_in_backend(backend_network):
873 198d91c3 Christos Stavrakakis
    try:
874 198d91c3 Christos Stavrakakis
        get_network_info(backend_network)
875 198d91c3 Christos Stavrakakis
        return True
876 d2036274 Christos Stavrakakis
    except rapi.GanetiApiError as e:
877 198d91c3 Christos Stavrakakis
        if e.code == 404:
878 198d91c3 Christos Stavrakakis
            return False
879 f533f224 Vangelis Koukis
880 c25cc9ec Vangelis Koukis
881 1cb7846c Christos Stavrakakis
def job_is_still_running(vm, job_id=None):
882 01f5f8d9 Christos Stavrakakis
    with pooled_rapi_client(vm) as c:
883 01f5f8d9 Christos Stavrakakis
        try:
884 1cb7846c Christos Stavrakakis
            if job_id is None:
885 1cb7846c Christos Stavrakakis
                job_id = vm.backendjobid
886 1cb7846c Christos Stavrakakis
            job_info = c.GetJobStatus(job_id)
887 a1dae38d Christos Stavrakakis
            return not (job_info["status"] in rapi.JOB_STATUS_FINALIZED)
888 a1dae38d Christos Stavrakakis
        except rapi.GanetiApiError:
889 01f5f8d9 Christos Stavrakakis
            return False
890 01f5f8d9 Christos Stavrakakis
891 01f5f8d9 Christos Stavrakakis
892 c583d487 Christos Stavrakakis
def disk_is_stale(vm, disk, timeout=60):
893 c583d487 Christos Stavrakakis
    """Check if a disk is stale or exists in the Ganeti backend."""
894 c583d487 Christos Stavrakakis
    # First check the state of the disk
895 c583d487 Christos Stavrakakis
    if disk.status == "CREATING":
896 c583d487 Christos Stavrakakis
        if datetime.now() < disk.created + timedelta(seconds=timeout):
897 c583d487 Christos Stavrakakis
            # Do not check for too recent disks to avoid the time overhead
898 c583d487 Christos Stavrakakis
            return False
899 c583d487 Christos Stavrakakis
        if job_is_still_running(vm, job_id=disk.backendjobid):
900 c583d487 Christos Stavrakakis
            return False
901 c583d487 Christos Stavrakakis
        else:
902 c583d487 Christos Stavrakakis
            # If job has finished, check that the disk exists, because the
903 c583d487 Christos Stavrakakis
            # message may have been lost or stuck in the queue.
904 c583d487 Christos Stavrakakis
            vm_info = get_instance_info(vm)
905 c583d487 Christos Stavrakakis
            if disk.backend_volume_uuid in vm_info["disk.names"]:
906 c583d487 Christos Stavrakakis
                return False
907 c583d487 Christos Stavrakakis
    return True
908 c583d487 Christos Stavrakakis
909 c583d487 Christos Stavrakakis
910 1cb7846c Christos Stavrakakis
def nic_is_stale(vm, nic, timeout=60):
911 1cb7846c Christos Stavrakakis
    """Check if a NIC is stale or exists in the Ganeti backend."""
912 1cb7846c Christos Stavrakakis
    # First check the state of the NIC and if there is a pending CONNECT
913 1cb7846c Christos Stavrakakis
    if nic.state == "BUILD" and vm.task == "CONNECT":
914 1cb7846c Christos Stavrakakis
        if datetime.now() < nic.created + timedelta(seconds=timeout):
915 1cb7846c Christos Stavrakakis
            # Do not check for too recent NICs to avoid the time overhead
916 1cb7846c Christos Stavrakakis
            return False
917 1cb7846c Christos Stavrakakis
        if job_is_still_running(vm, job_id=vm.task_job_id):
918 1cb7846c Christos Stavrakakis
            return False
919 1cb7846c Christos Stavrakakis
        else:
920 1cb7846c Christos Stavrakakis
            # If job has finished, check that the NIC exists, because the
921 1cb7846c Christos Stavrakakis
            # message may have been lost or stuck in the queue.
922 1cb7846c Christos Stavrakakis
            vm_info = get_instance_info(vm)
923 1cb7846c Christos Stavrakakis
            if nic.backend_uuid in vm_info["nic.names"]:
924 1cb7846c Christos Stavrakakis
                return False
925 1cb7846c Christos Stavrakakis
    return True
926 1cb7846c Christos Stavrakakis
927 1cb7846c Christos Stavrakakis
928 d2036274 Christos Stavrakakis
def ensure_network_is_active(backend, network_id):
929 d2036274 Christos Stavrakakis
    """Ensure that a network is active in the specified backend
930 d2036274 Christos Stavrakakis

931 d2036274 Christos Stavrakakis
    Check that a network exists and is active in the specified backend. If not
932 d2036274 Christos Stavrakakis
    (re-)create the network. Return the corresponding BackendNetwork object
933 d2036274 Christos Stavrakakis
    and the IDs of the Ganeti job to create the network.
934 d2036274 Christos Stavrakakis

935 d2036274 Christos Stavrakakis
    """
936 d2036274 Christos Stavrakakis
    job_ids = []
937 40a815f8 Christos Stavrakakis
    try:
938 40a815f8 Christos Stavrakakis
        bnet = BackendNetwork.objects.select_related("network")\
939 40a815f8 Christos Stavrakakis
                                     .get(backend=backend, network=network_id)
940 40a815f8 Christos Stavrakakis
        if bnet.operstate != "ACTIVE":
941 40a815f8 Christos Stavrakakis
            job_ids = create_network(bnet.network, backend, connect=True)
942 40a815f8 Christos Stavrakakis
    except BackendNetwork.DoesNotExist:
943 40a815f8 Christos Stavrakakis
        network = Network.objects.select_for_update().get(id=network_id)
944 40a815f8 Christos Stavrakakis
        bnet = BackendNetwork.objects.create(backend=backend, network=network)
945 d2036274 Christos Stavrakakis
        job_ids = create_network(network, backend, connect=True)
946 d2036274 Christos Stavrakakis
947 d2036274 Christos Stavrakakis
    return bnet, job_ids
948 d2036274 Christos Stavrakakis
949 d2036274 Christos Stavrakakis
950 99af08a4 Christos Stavrakakis
def create_network(network, backend, connect=True):
951 99af08a4 Christos Stavrakakis
    """Create a network in a Ganeti backend"""
952 99af08a4 Christos Stavrakakis
    log.debug("Creating network %s in backend %s", network, backend)
953 64938cb0 Giorgos Verigakis
954 99af08a4 Christos Stavrakakis
    job_id = _create_network(network, backend)
955 c25cc9ec Vangelis Koukis
956 99af08a4 Christos Stavrakakis
    if connect:
957 99af08a4 Christos Stavrakakis
        job_ids = connect_network(network, backend, depends=[job_id])
958 99af08a4 Christos Stavrakakis
        return job_ids
959 99af08a4 Christos Stavrakakis
    else:
960 99af08a4 Christos Stavrakakis
        return [job_id]
961 37ca953f Christodoulos Psaltis
962 64938cb0 Giorgos Verigakis
963 3524241a Christos Stavrakakis
def _create_network(network, backend):
964 3524241a Christos Stavrakakis
    """Create a network."""
965 c25cc9ec Vangelis Koukis
966 22ee6892 Christos Stavrakakis
    tags = network.backend_tag
967 92d2d1ce Christos Stavrakakis
    subnet = None
968 92d2d1ce Christos Stavrakakis
    subnet6 = None
969 92d2d1ce Christos Stavrakakis
    gateway = None
970 92d2d1ce Christos Stavrakakis
    gateway6 = None
971 3278725f Christos Stavrakakis
    for _subnet in network.subnets.all():
972 a2bd0802 Christos Stavrakakis
        if _subnet.dhcp and not "nfdhcpd" in tags:
973 a2bd0802 Christos Stavrakakis
            tags.append("nfdhcpd")
974 3278725f Christos Stavrakakis
        if _subnet.ipversion == 4:
975 fe3b2809 Christos Stavrakakis
            subnet = _subnet.cidr
976 fe3b2809 Christos Stavrakakis
            gateway = _subnet.gateway
977 3278725f Christos Stavrakakis
        elif _subnet.ipversion == 6:
978 fe3b2809 Christos Stavrakakis
            subnet6 = _subnet.cidr
979 fe3b2809 Christos Stavrakakis
            gateway6 = _subnet.gateway
980 2d762302 Dimitris Aragiorgis
981 a3acfc5b Christos Stavrakakis
    conflicts_check = False
982 2d762302 Dimitris Aragiorgis
    if network.public:
983 700b85be Dimitris Aragiorgis
        tags.append('public')
984 a3acfc5b Christos Stavrakakis
        if subnet is not None:
985 a3acfc5b Christos Stavrakakis
            conflicts_check = True
986 2d762302 Dimitris Aragiorgis
    else:
987 700b85be Dimitris Aragiorgis
        tags.append('private')
988 22ee6892 Christos Stavrakakis
989 5aeb4e93 Christos Stavrakakis
    # Use a dummy network subnet for IPv6 only networks. Currently Ganeti does
990 5aeb4e93 Christos Stavrakakis
    # not support IPv6 only networks. To bypass this limitation, we create the
991 5aeb4e93 Christos Stavrakakis
    # network with a dummy network subnet, and make Cyclades connect instances
992 5aeb4e93 Christos Stavrakakis
    # to such networks, with address=None.
993 5aeb4e93 Christos Stavrakakis
    if subnet is None:
994 c12627c7 Christos Stavrakakis
        subnet = "10.0.0.0/29"
995 5aeb4e93 Christos Stavrakakis
996 3524241a Christos Stavrakakis
    try:
997 3524241a Christos Stavrakakis
        bn = BackendNetwork.objects.get(network=network, backend=backend)
998 3524241a Christos Stavrakakis
        mac_prefix = bn.mac_prefix
999 3524241a Christos Stavrakakis
    except BackendNetwork.DoesNotExist:
1000 cc92b70f Christos Stavrakakis
        raise Exception("BackendNetwork for network '%s' in backend '%s'"
1001 3524241a Christos Stavrakakis
                        " does not exist" % (network.id, backend.id))
1002 3524241a Christos Stavrakakis
1003 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1004 3524241a Christos Stavrakakis
        return client.CreateNetwork(network_name=network.backend_id,
1005 5aeb4e93 Christos Stavrakakis
                                    network=subnet,
1006 92d2d1ce Christos Stavrakakis
                                    network6=subnet6,
1007 92d2d1ce Christos Stavrakakis
                                    gateway=gateway,
1008 92d2d1ce Christos Stavrakakis
                                    gateway6=gateway6,
1009 3524241a Christos Stavrakakis
                                    mac_prefix=mac_prefix,
1010 2d762302 Dimitris Aragiorgis
                                    conflicts_check=conflicts_check,
1011 3524241a Christos Stavrakakis
                                    tags=tags)
1012 3524241a Christos Stavrakakis
1013 3524241a Christos Stavrakakis
1014 99af08a4 Christos Stavrakakis
def connect_network(network, backend, depends=[], group=None):
1015 3524241a Christos Stavrakakis
    """Connect a network to nodegroups."""
1016 bf5c82dc Christos Stavrakakis
    log.debug("Connecting network %s to backend %s", network, backend)
1017 bf5c82dc Christos Stavrakakis
1018 a3acfc5b Christos Stavrakakis
    conflicts_check = False
1019 a3acfc5b Christos Stavrakakis
    if network.public and (network.subnet4 is not None):
1020 2d762302 Dimitris Aragiorgis
        conflicts_check = True
1021 2d762302 Dimitris Aragiorgis
1022 d2036274 Christos Stavrakakis
    depends = create_job_dependencies(depends)
1023 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1024 99af08a4 Christos Stavrakakis
        groups = [group] if group is not None else client.GetGroups()
1025 99af08a4 Christos Stavrakakis
        job_ids = []
1026 99af08a4 Christos Stavrakakis
        for group in groups:
1027 99af08a4 Christos Stavrakakis
            job_id = client.ConnectNetwork(network.backend_id, group,
1028 99af08a4 Christos Stavrakakis
                                           network.mode, network.link,
1029 99af08a4 Christos Stavrakakis
                                           conflicts_check,
1030 99af08a4 Christos Stavrakakis
                                           depends=depends)
1031 99af08a4 Christos Stavrakakis
            job_ids.append(job_id)
1032 99af08a4 Christos Stavrakakis
    return job_ids
1033 22ee6892 Christos Stavrakakis
1034 22ee6892 Christos Stavrakakis
1035 99af08a4 Christos Stavrakakis
def delete_network(network, backend, disconnect=True):
1036 99af08a4 Christos Stavrakakis
    log.debug("Deleting network %s from backend %s", network, backend)
1037 22ee6892 Christos Stavrakakis
1038 99af08a4 Christos Stavrakakis
    depends = []
1039 99af08a4 Christos Stavrakakis
    if disconnect:
1040 99af08a4 Christos Stavrakakis
        depends = disconnect_network(network, backend)
1041 99af08a4 Christos Stavrakakis
    _delete_network(network, backend, depends=depends)
1042 bf5c82dc Christos Stavrakakis
1043 22ee6892 Christos Stavrakakis
1044 99af08a4 Christos Stavrakakis
def _delete_network(network, backend, depends=[]):
1045 d2036274 Christos Stavrakakis
    depends = create_job_dependencies(depends)
1046 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1047 99af08a4 Christos Stavrakakis
        return client.DeleteNetwork(network.backend_id, depends)
1048 22ee6892 Christos Stavrakakis
1049 22ee6892 Christos Stavrakakis
1050 3524241a Christos Stavrakakis
def disconnect_network(network, backend, group=None):
1051 bf5c82dc Christos Stavrakakis
    log.debug("Disconnecting network %s to backend %s", network, backend)
1052 22ee6892 Christos Stavrakakis
1053 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1054 99af08a4 Christos Stavrakakis
        groups = [group] if group is not None else client.GetGroups()
1055 99af08a4 Christos Stavrakakis
        job_ids = []
1056 99af08a4 Christos Stavrakakis
        for group in groups:
1057 99af08a4 Christos Stavrakakis
            job_id = client.DisconnectNetwork(network.backend_id, group)
1058 99af08a4 Christos Stavrakakis
            job_ids.append(job_id)
1059 99af08a4 Christos Stavrakakis
    return job_ids
1060 36f4cb29 Christos Stavrakakis
1061 36f4cb29 Christos Stavrakakis
1062 2a2b01e5 Christos Stavrakakis
def connect_to_network(vm, nic):
1063 2a2b01e5 Christos Stavrakakis
    network = nic.network
1064 99af08a4 Christos Stavrakakis
    backend = vm.backend
1065 d2036274 Christos Stavrakakis
    bnet, depend_jobs = ensure_network_is_active(backend, network.id)
1066 99af08a4 Christos Stavrakakis
1067 d2036274 Christos Stavrakakis
    depends = create_job_dependencies(depend_jobs)
1068 99af08a4 Christos Stavrakakis
1069 7c714455 Christos Stavrakakis
    nic = {'name': nic.backend_uuid,
1070 7c714455 Christos Stavrakakis
           'network': network.backend_id,
1071 8764d304 Christos Stavrakakis
           'ip': nic.ipv4_address}
1072 22ee6892 Christos Stavrakakis
1073 7c714455 Christos Stavrakakis
    log.debug("Adding NIC %s to VM %s", nic, vm)
1074 22ee6892 Christos Stavrakakis
1075 6488097c Christos Stavrakakis
    kwargs = {
1076 6488097c Christos Stavrakakis
        "instance": vm.backend_vm_id,
1077 1d74f135 Dimitris Aragiorgis
        "nics": [("add", "-1", nic)],
1078 6488097c Christos Stavrakakis
        "depends": depends,
1079 6488097c Christos Stavrakakis
    }
1080 6488097c Christos Stavrakakis
    if vm.backend.use_hotplug():
1081 b81e0ba5 Christos Stavrakakis
        kwargs["hotplug_if_possible"] = True
1082 6488097c Christos Stavrakakis
    if settings.TEST:
1083 6488097c Christos Stavrakakis
        kwargs["dry_run"] = True
1084 6488097c Christos Stavrakakis
1085 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
1086 6488097c Christos Stavrakakis
        return client.ModifyInstance(**kwargs)
1087 22ee6892 Christos Stavrakakis
1088 22ee6892 Christos Stavrakakis
1089 3524241a Christos Stavrakakis
def disconnect_from_network(vm, nic):
1090 7c714455 Christos Stavrakakis
    log.debug("Removing NIC %s of VM %s", nic, vm)
1091 22ee6892 Christos Stavrakakis
1092 6488097c Christos Stavrakakis
    kwargs = {
1093 6488097c Christos Stavrakakis
        "instance": vm.backend_vm_id,
1094 cd7ed999 Christos Stavrakakis
        "nics": [("remove", nic.backend_uuid, {})],
1095 6488097c Christos Stavrakakis
    }
1096 6488097c Christos Stavrakakis
    if vm.backend.use_hotplug():
1097 b81e0ba5 Christos Stavrakakis
        kwargs["hotplug_if_possible"] = True
1098 6488097c Christos Stavrakakis
    if settings.TEST:
1099 6488097c Christos Stavrakakis
        kwargs["dry_run"] = True
1100 6488097c Christos Stavrakakis
1101 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
1102 6488097c Christos Stavrakakis
        jobID = client.ModifyInstance(**kwargs)
1103 27435d42 Christos Stavrakakis
        firewall_profile = nic.firewall_profile
1104 231b0fb6 Christos Stavrakakis
        if firewall_profile and firewall_profile != "DISABLED":
1105 d0545590 Christos Stavrakakis
            tag = _firewall_tags[firewall_profile] % nic.backend_uuid
1106 27435d42 Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, [tag],
1107 27435d42 Christos Stavrakakis
                                      dry_run=settings.TEST)
1108 27435d42 Christos Stavrakakis
1109 27435d42 Christos Stavrakakis
        return jobID
1110 91826390 Giorgos Verigakis
1111 c25cc9ec Vangelis Koukis
1112 d0545590 Christos Stavrakakis
def set_firewall_profile(vm, profile, nic):
1113 d0545590 Christos Stavrakakis
    uuid = nic.backend_uuid
1114 26563957 Giorgos Verigakis
    try:
1115 d0545590 Christos Stavrakakis
        tag = _firewall_tags[profile] % uuid
1116 26563957 Giorgos Verigakis
    except KeyError:
1117 91826390 Giorgos Verigakis
        raise ValueError("Unsopported Firewall Profile: %s" % profile)
1118 37ca953f Christodoulos Psaltis
1119 d0545590 Christos Stavrakakis
    log.debug("Setting tag of VM %s, NIC %s, to %s", vm, nic, profile)
1120 bf5c82dc Christos Stavrakakis
1121 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
1122 b2791a77 Christos Stavrakakis
        # Delete previous firewall tags
1123 b2791a77 Christos Stavrakakis
        old_tags = client.GetInstanceTags(vm.backend_vm_id)
1124 d0545590 Christos Stavrakakis
        delete_tags = [(t % uuid) for t in _firewall_tags.values()
1125 d0545590 Christos Stavrakakis
                       if (t % uuid) in old_tags]
1126 b2791a77 Christos Stavrakakis
        if delete_tags:
1127 b2791a77 Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, delete_tags,
1128 3524241a Christos Stavrakakis
                                      dry_run=settings.TEST)
1129 37ca953f Christodoulos Psaltis
1130 27435d42 Christos Stavrakakis
        if profile != "DISABLED":
1131 27435d42 Christos Stavrakakis
            client.AddInstanceTags(vm.backend_vm_id, [tag],
1132 27435d42 Christos Stavrakakis
                                   dry_run=settings.TEST)
1133 9afeb669 Kostas Papadimitriou
1134 3524241a Christos Stavrakakis
        # XXX NOP ModifyInstance call to force process_net_status to run
1135 3524241a Christos Stavrakakis
        # on the dispatcher
1136 cc92b70f Christos Stavrakakis
        os_name = settings.GANETI_CREATEINSTANCE_KWARGS['os']
1137 3524241a Christos Stavrakakis
        client.ModifyInstance(vm.backend_vm_id,
1138 cc92b70f Christos Stavrakakis
                              os_name=os_name)
1139 41a7fae7 Christos Stavrakakis
    return None
1140 5eedb0e4 Vangelis Koukis
1141 41303ed0 Vangelis Koukis
1142 dec501fa Christos Stavrakakis
def attach_volume(vm, volume, depends=[]):
1143 ef0839e9 Christos Stavrakakis
    log.debug("Attaching volume %s to vm %s", volume, vm)
1144 dec501fa Christos Stavrakakis
1145 5f90e24c Christos Stavrakakis
    disk = {"size": int(volume.size) << 10,
1146 dec501fa Christos Stavrakakis
            "name": volume.backend_volume_uuid,
1147 dec501fa Christos Stavrakakis
            "volume_name": volume.backend_volume_uuid}
1148 5f90e24c Christos Stavrakakis
1149 a868c831 Christos Stavrakakis
    disk_provider = volume.provider
1150 5f90e24c Christos Stavrakakis
    if disk_provider is not None:
1151 5f90e24c Christos Stavrakakis
        disk["provider"] = disk_provider
1152 5f90e24c Christos Stavrakakis
1153 5f90e24c Christos Stavrakakis
    if volume.origin is not None:
1154 5f90e24c Christos Stavrakakis
        disk["origin"] = volume.origin
1155 dec501fa Christos Stavrakakis
1156 dec501fa Christos Stavrakakis
    kwargs = {
1157 dec501fa Christos Stavrakakis
        "instance": vm.backend_vm_id,
1158 dec501fa Christos Stavrakakis
        "disks": [("add", "-1", disk)],
1159 dec501fa Christos Stavrakakis
        "depends": depends,
1160 dec501fa Christos Stavrakakis
    }
1161 dec501fa Christos Stavrakakis
    if vm.backend.use_hotplug():
1162 5f90e24c Christos Stavrakakis
        kwargs["hotplug_if_possible"] = True
1163 dec501fa Christos Stavrakakis
    if settings.TEST:
1164 dec501fa Christos Stavrakakis
        kwargs["dry_run"] = True
1165 dec501fa Christos Stavrakakis
1166 dec501fa Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
1167 dec501fa Christos Stavrakakis
        return client.ModifyInstance(**kwargs)
1168 dec501fa Christos Stavrakakis
1169 dec501fa Christos Stavrakakis
1170 5f90e24c Christos Stavrakakis
def detach_volume(vm, volume, depends=[]):
1171 dec501fa Christos Stavrakakis
    log.debug("Removing volume %s from vm %s", volume, vm)
1172 dec501fa Christos Stavrakakis
    kwargs = {
1173 dec501fa Christos Stavrakakis
        "instance": vm.backend_vm_id,
1174 dec501fa Christos Stavrakakis
        "disks": [("remove", volume.backend_volume_uuid, {})],
1175 5f90e24c Christos Stavrakakis
        "depends": depends,
1176 dec501fa Christos Stavrakakis
    }
1177 dec501fa Christos Stavrakakis
    if vm.backend.use_hotplug():
1178 5f90e24c Christos Stavrakakis
        kwargs["hotplug_if_possible"] = True
1179 dec501fa Christos Stavrakakis
    if settings.TEST:
1180 dec501fa Christos Stavrakakis
        kwargs["dry_run"] = True
1181 dec501fa Christos Stavrakakis
1182 dec501fa Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
1183 dec501fa Christos Stavrakakis
        return client.ModifyInstance(**kwargs)
1184 dec501fa Christos Stavrakakis
1185 dec501fa Christos Stavrakakis
1186 1316db51 Christos
def snapshot_instance(vm, snapshot_name):
1187 1316db51 Christos
    #volume = instance.volumes.all()[0]
1188 1316db51 Christos
    with pooled_rapi_client(vm) as client:
1189 1316db51 Christos
        return client.SnapshotInstance(instance=vm.backend_vm_id,
1190 1316db51 Christos
                                       snapshot_name=snapshot_name)
1191 1316db51 Christos
1192 1316db51 Christos
1193 e77a29ab Christos Stavrakakis
def get_instances(backend, bulk=True):
1194 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
1195 e77a29ab Christos Stavrakakis
        return c.GetInstances(bulk=bulk)
1196 d986cb32 Christos Stavrakakis
1197 f5b4f2a3 Christos Stavrakakis
1198 e77a29ab Christos Stavrakakis
def get_nodes(backend, bulk=True):
1199 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
1200 e77a29ab Christos Stavrakakis
        return c.GetNodes(bulk=bulk)
1201 1a894bfe Christos Stavrakakis
1202 1a894bfe Christos Stavrakakis
1203 70a0afab Christos Stavrakakis
def get_jobs(backend, bulk=True):
1204 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
1205 70a0afab Christos Stavrakakis
        return c.GetJobs(bulk=bulk)
1206 f5b4f2a3 Christos Stavrakakis
1207 17852fe9 Giorgos Verigakis
1208 1a894bfe Christos Stavrakakis
def get_physical_resources(backend):
1209 1a894bfe Christos Stavrakakis
    """ Get the physical resources of a backend.
1210 1a894bfe Christos Stavrakakis

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

1213 1a894bfe Christos Stavrakakis
    """
1214 e77a29ab Christos Stavrakakis
    nodes = get_nodes(backend, bulk=True)
1215 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
1216 1a894bfe Christos Stavrakakis
    res = {}
1217 1a894bfe Christos Stavrakakis
    for a in attr:
1218 1a894bfe Christos Stavrakakis
        res[a] = 0
1219 1a894bfe Christos Stavrakakis
    for n in nodes:
1220 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
1221 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
1222 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
1223 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
1224 1a894bfe Christos Stavrakakis
            for a in attr:
1225 121b8921 Christos Stavrakakis
                res[a] += int(n[a] or 0)
1226 1a894bfe Christos Stavrakakis
    return res
1227 1a894bfe Christos Stavrakakis
1228 1a894bfe Christos Stavrakakis
1229 1da50fe3 Christos Stavrakakis
def update_backend_resources(backend, resources=None):
1230 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
1231 1a894bfe Christos Stavrakakis

1232 1a894bfe Christos Stavrakakis
    """
1233 17852fe9 Giorgos Verigakis
1234 1a894bfe Christos Stavrakakis
    if not resources:
1235 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
1236 41303ed0 Vangelis Koukis
1237 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
1238 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
1239 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
1240 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
1241 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
1242 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
1243 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
1244 1a894bfe Christos Stavrakakis
    backend.save()
1245 1a894bfe Christos Stavrakakis
1246 1a894bfe Christos Stavrakakis
1247 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
1248 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
1249 1a894bfe Christos Stavrakakis

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

1253 1a894bfe Christos Stavrakakis
    """
1254 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1255 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
1256 1a894bfe Christos Stavrakakis
    mem = 0
1257 1a894bfe Christos Stavrakakis
    for i in instances:
1258 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
1259 1a894bfe Christos Stavrakakis
    return mem
1260 b3d28af2 Christos Stavrakakis
1261 1da50fe3 Christos Stavrakakis
1262 1da50fe3 Christos Stavrakakis
def get_available_disk_templates(backend):
1263 1da50fe3 Christos Stavrakakis
    """Get the list of available disk templates of a Ganeti backend.
1264 1da50fe3 Christos Stavrakakis

1265 1da50fe3 Christos Stavrakakis
    The list contains the disk templates that are enabled in the Ganeti backend
1266 1da50fe3 Christos Stavrakakis
    and also included in ipolicy-disk-templates.
1267 1da50fe3 Christos Stavrakakis

1268 1da50fe3 Christos Stavrakakis
    """
1269 1da50fe3 Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
1270 1da50fe3 Christos Stavrakakis
        info = c.GetInfo()
1271 1da50fe3 Christos Stavrakakis
    ipolicy_disk_templates = info["ipolicy"]["disk-templates"]
1272 a8ae6989 Christos Stavrakakis
    try:
1273 a8ae6989 Christos Stavrakakis
        enabled_disk_templates = info["enabled_disk_templates"]
1274 a8ae6989 Christos Stavrakakis
        return [dp for dp in enabled_disk_templates
1275 a8ae6989 Christos Stavrakakis
                if dp in ipolicy_disk_templates]
1276 a8ae6989 Christos Stavrakakis
    except KeyError:
1277 a8ae6989 Christos Stavrakakis
        # Ganeti < 2.8 does not have 'enabled_disk_templates'
1278 a8ae6989 Christos Stavrakakis
        return ipolicy_disk_templates
1279 1da50fe3 Christos Stavrakakis
1280 1da50fe3 Christos Stavrakakis
1281 1da50fe3 Christos Stavrakakis
def update_backend_disk_templates(backend):
1282 1da50fe3 Christos Stavrakakis
    disk_templates = get_available_disk_templates(backend)
1283 1da50fe3 Christos Stavrakakis
    backend.disk_templates = disk_templates
1284 1da50fe3 Christos Stavrakakis
    backend.save()
1285 1da50fe3 Christos Stavrakakis
1286 1da50fe3 Christos Stavrakakis
1287 b3d28af2 Christos Stavrakakis
##
1288 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
1289 b3d28af2 Christos Stavrakakis
##
1290 b3d28af2 Christos Stavrakakis
1291 b3d28af2 Christos Stavrakakis
1292 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
1293 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
1294 d2036274 Christos Stavrakakis
    if result[0] != rapi.JOB_STATUS_SUCCESS:
1295 b3d28af2 Christos Stavrakakis
        return result
1296 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
1297 b3d28af2 Christos Stavrakakis
    return result
1298 b3d28af2 Christos Stavrakakis
1299 b3d28af2 Christos Stavrakakis
1300 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
1301 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1302 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
1303 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
1304 3524241a Christos Stavrakakis
    return result
1305 b3d28af2 Christos Stavrakakis
1306 b3d28af2 Christos Stavrakakis
1307 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
1308 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1309 3524241a Christos Stavrakakis
        for group in client.GetGroups():
1310 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
1311 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
1312 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
1313 d2036274 Christos Stavrakakis
            if result[0] != rapi.JOB_STATUS_SUCCESS:
1314 3524241a Christos Stavrakakis
                return result
1315 b3d28af2 Christos Stavrakakis
1316 b3d28af2 Christos Stavrakakis
    return result
1317 b3d28af2 Christos Stavrakakis
1318 b3d28af2 Christos Stavrakakis
1319 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
1320 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
1321 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
1322 d2036274 Christos Stavrakakis
    while status not in rapi.JOB_STATUS_FINALIZED:
1323 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
1324 cc92b70f Christos Stavrakakis
                                         [result], None)
1325 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
1326 b3d28af2 Christos Stavrakakis
1327 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS:
1328 b3d28af2 Christos Stavrakakis
        return (status, None)
1329 b3d28af2 Christos Stavrakakis
    else:
1330 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
1331 b3d28af2 Christos Stavrakakis
        return (status, error)
1332 d2036274 Christos Stavrakakis
1333 d2036274 Christos Stavrakakis
1334 d2036274 Christos Stavrakakis
def create_job_dependencies(job_ids=[], job_states=None):
1335 d2036274 Christos Stavrakakis
    """Transform a list of job IDs to Ganeti 'depends' attribute."""
1336 d2036274 Christos Stavrakakis
    if job_states is None:
1337 d2036274 Christos Stavrakakis
        job_states = list(rapi.JOB_STATUS_FINALIZED)
1338 d2036274 Christos Stavrakakis
    assert(type(job_states) == list)
1339 d2036274 Christos Stavrakakis
    return [[job_id, job_states] for job_id in job_ids]