Statistics
| Branch: | Tag: | Revision:

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

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

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

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

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

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

254 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
255 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
256 ad2d6807 Vangelis Koukis

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

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

393 3278725f Christos Stavrakakis
    Remove all IP addresses that are associated with the NetworkInterface
394 3278725f Christos Stavrakakis
    object, by returning them to the pool and deleting the IPAddress object. If
395 3278725f Christos Stavrakakis
    the IP is a floating IP, then it is just disassociated from the NIC.
396 bfb3f9c2 Christos Stavrakakis
    If version is specified, then only IP addressses of that version will be
397 bfb3f9c2 Christos Stavrakakis
    removed.
398 a1baa42b Christos Stavrakakis

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

452 c583d487 Christos Stavrakakis
    Process an incoming message from the Ganeti backend,
453 c583d487 Christos Stavrakakis
    detailing the disk configuration of a VM instance.
454 c583d487 Christos Stavrakakis

455 c583d487 Christos Stavrakakis
    Update the state of the VM in the DB accordingly.
456 c583d487 Christos Stavrakakis

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

580 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
581 99af08a4 Christos Stavrakakis
    backends that network exists.
582 99af08a4 Christos Stavrakakis

583 99af08a4 Christos Stavrakakis
    The state of the network is:
584 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
585 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
586 99af08a4 Christos Stavrakakis

587 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
588 99af08a4 Christos Stavrakakis
    quotas for the network.
589 99af08a4 Christos Stavrakakis

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

711 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
712 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
713 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
714 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
715 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
716 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
717 7d43565f Kostas Papadimitriou
    """
718 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
719 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
720 cc92b70f Christos Stavrakakis
                                                   message=message,
721 cc92b70f Christos Stavrakakis
                                                   details=details)
722 7d43565f Kostas Papadimitriou
723 7d43565f Kostas Papadimitriou
724 e7953d63 Christos Stavrakakis
def create_instance(vm, nics, volumes, flavor, image):
725 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
726 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
727 7f691719 Christos Stavrakakis

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

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

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

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

1242 1a894bfe Christos Stavrakakis
    """
1243 e77a29ab Christos Stavrakakis
    nodes = get_nodes(backend, bulk=True)
1244 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
1245 1a894bfe Christos Stavrakakis
    res = {}
1246 1a894bfe Christos Stavrakakis
    for a in attr:
1247 1a894bfe Christos Stavrakakis
        res[a] = 0
1248 1a894bfe Christos Stavrakakis
    for n in nodes:
1249 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
1250 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
1251 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
1252 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
1253 1a894bfe Christos Stavrakakis
            for a in attr:
1254 121b8921 Christos Stavrakakis
                res[a] += int(n[a] or 0)
1255 1a894bfe Christos Stavrakakis
    return res
1256 1a894bfe Christos Stavrakakis
1257 1a894bfe Christos Stavrakakis
1258 1da50fe3 Christos Stavrakakis
def update_backend_resources(backend, resources=None):
1259 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
1260 1a894bfe Christos Stavrakakis

1261 1a894bfe Christos Stavrakakis
    """
1262 17852fe9 Giorgos Verigakis
1263 1a894bfe Christos Stavrakakis
    if not resources:
1264 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
1265 41303ed0 Vangelis Koukis
1266 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
1267 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
1268 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
1269 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
1270 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
1271 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
1272 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
1273 1a894bfe Christos Stavrakakis
    backend.save()
1274 1a894bfe Christos Stavrakakis
1275 1a894bfe Christos Stavrakakis
1276 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
1277 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
1278 1a894bfe Christos Stavrakakis

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

1282 1a894bfe Christos Stavrakakis
    """
1283 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1284 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
1285 1a894bfe Christos Stavrakakis
    mem = 0
1286 1a894bfe Christos Stavrakakis
    for i in instances:
1287 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
1288 1a894bfe Christos Stavrakakis
    return mem
1289 b3d28af2 Christos Stavrakakis
1290 1da50fe3 Christos Stavrakakis
1291 1da50fe3 Christos Stavrakakis
def get_available_disk_templates(backend):
1292 1da50fe3 Christos Stavrakakis
    """Get the list of available disk templates of a Ganeti backend.
1293 1da50fe3 Christos Stavrakakis

1294 1da50fe3 Christos Stavrakakis
    The list contains the disk templates that are enabled in the Ganeti backend
1295 1da50fe3 Christos Stavrakakis
    and also included in ipolicy-disk-templates.
1296 1da50fe3 Christos Stavrakakis

1297 1da50fe3 Christos Stavrakakis
    """
1298 1da50fe3 Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
1299 1da50fe3 Christos Stavrakakis
        info = c.GetInfo()
1300 1da50fe3 Christos Stavrakakis
    ipolicy_disk_templates = info["ipolicy"]["disk-templates"]
1301 a8ae6989 Christos Stavrakakis
    try:
1302 a8ae6989 Christos Stavrakakis
        enabled_disk_templates = info["enabled_disk_templates"]
1303 a8ae6989 Christos Stavrakakis
        return [dp for dp in enabled_disk_templates
1304 a8ae6989 Christos Stavrakakis
                if dp in ipolicy_disk_templates]
1305 a8ae6989 Christos Stavrakakis
    except KeyError:
1306 a8ae6989 Christos Stavrakakis
        # Ganeti < 2.8 does not have 'enabled_disk_templates'
1307 a8ae6989 Christos Stavrakakis
        return ipolicy_disk_templates
1308 1da50fe3 Christos Stavrakakis
1309 1da50fe3 Christos Stavrakakis
1310 1da50fe3 Christos Stavrakakis
def update_backend_disk_templates(backend):
1311 1da50fe3 Christos Stavrakakis
    disk_templates = get_available_disk_templates(backend)
1312 1da50fe3 Christos Stavrakakis
    backend.disk_templates = disk_templates
1313 1da50fe3 Christos Stavrakakis
    backend.save()
1314 1da50fe3 Christos Stavrakakis
1315 1da50fe3 Christos Stavrakakis
1316 b3d28af2 Christos Stavrakakis
##
1317 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
1318 b3d28af2 Christos Stavrakakis
##
1319 b3d28af2 Christos Stavrakakis
1320 b3d28af2 Christos Stavrakakis
1321 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
1322 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
1323 d2036274 Christos Stavrakakis
    if result[0] != rapi.JOB_STATUS_SUCCESS:
1324 b3d28af2 Christos Stavrakakis
        return result
1325 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
1326 b3d28af2 Christos Stavrakakis
    return result
1327 b3d28af2 Christos Stavrakakis
1328 b3d28af2 Christos Stavrakakis
1329 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
1330 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1331 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
1332 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
1333 3524241a Christos Stavrakakis
    return result
1334 b3d28af2 Christos Stavrakakis
1335 b3d28af2 Christos Stavrakakis
1336 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
1337 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1338 3524241a Christos Stavrakakis
        for group in client.GetGroups():
1339 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
1340 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
1341 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
1342 d2036274 Christos Stavrakakis
            if result[0] != rapi.JOB_STATUS_SUCCESS:
1343 3524241a Christos Stavrakakis
                return result
1344 b3d28af2 Christos Stavrakakis
1345 b3d28af2 Christos Stavrakakis
    return result
1346 b3d28af2 Christos Stavrakakis
1347 b3d28af2 Christos Stavrakakis
1348 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
1349 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
1350 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
1351 d2036274 Christos Stavrakakis
    while status not in rapi.JOB_STATUS_FINALIZED:
1352 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
1353 cc92b70f Christos Stavrakakis
                                         [result], None)
1354 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
1355 b3d28af2 Christos Stavrakakis
1356 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS:
1357 b3d28af2 Christos Stavrakakis
        return (status, None)
1358 b3d28af2 Christos Stavrakakis
    else:
1359 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
1360 b3d28af2 Christos Stavrakakis
        return (status, error)
1361 d2036274 Christos Stavrakakis
1362 d2036274 Christos Stavrakakis
1363 d2036274 Christos Stavrakakis
def create_job_dependencies(job_ids=[], job_states=None):
1364 d2036274 Christos Stavrakakis
    """Transform a list of job IDs to Ganeti 'depends' attribute."""
1365 d2036274 Christos Stavrakakis
    if job_states is None:
1366 d2036274 Christos Stavrakakis
        job_states = list(rapi.JOB_STATUS_FINALIZED)
1367 d2036274 Christos Stavrakakis
    assert(type(job_states) == list)
1368 d2036274 Christos Stavrakakis
    return [[job_id, job_states] for job_id in job_ids]