Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (36.5 kB)

1 41a7fae7 Christos Stavrakakis
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 37ca953f Christodoulos Psaltis
#
3 adee02b8 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 adee02b8 Giorgos Verigakis
# without modification, are permitted provided that the following
5 adee02b8 Giorgos Verigakis
# conditions are met:
6 37ca953f Christodoulos Psaltis
#
7 adee02b8 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 adee02b8 Giorgos Verigakis
#      disclaimer.
10 37ca953f Christodoulos Psaltis
#
11 adee02b8 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 adee02b8 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 adee02b8 Giorgos Verigakis
#      provided with the distribution.
15 37ca953f Christodoulos Psaltis
#
16 adee02b8 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 adee02b8 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 adee02b8 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 adee02b8 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 adee02b8 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 adee02b8 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 adee02b8 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 adee02b8 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 adee02b8 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 adee02b8 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 adee02b8 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 adee02b8 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 37ca953f Christodoulos Psaltis
#
29 adee02b8 Giorgos Verigakis
# The views and conclusions contained in the software and
30 adee02b8 Giorgos Verigakis
# documentation are those of the authors and should not be
31 adee02b8 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 adee02b8 Giorgos Verigakis
# or implied, of GRNET S.A.
33 529178b1 Giorgos Verigakis
from django.conf import settings
34 207b70d5 Giorgos Verigakis
from django.db import transaction
35 a1baa42b Christos Stavrakakis
from datetime import datetime, timedelta
36 207b70d5 Giorgos Verigakis
37 22ee6892 Christos Stavrakakis
from synnefo.db.models import (Backend, VirtualMachine, Network,
38 3524241a Christos Stavrakakis
                               BackendNetwork, BACKEND_STATUSES,
39 ca4d59e3 Christos Stavrakakis
                               pooled_rapi_client, VirtualMachineDiagnostic,
40 cb7b1c23 Christos Stavrakakis
                               Flavor, FloatingIP)
41 03992c72 Christos Stavrakakis
from synnefo.logic import utils
42 cb4eee84 Christos Stavrakakis
from synnefo import quotas
43 b7d38981 Dimitris Aragiorgis
from synnefo.api.util import release_resource
44 fd95834e Christos Stavrakakis
from synnefo.util.mac2eui64 import mac2eui64
45 198d91c3 Christos Stavrakakis
from synnefo.logic.rapi import GanetiApiError
46 529178b1 Giorgos Verigakis
47 3524241a Christos Stavrakakis
from logging import getLogger
48 3524241a Christos Stavrakakis
log = getLogger(__name__)
49 9e98ba3c Giorgos Verigakis
50 529178b1 Giorgos Verigakis
51 efff6193 Giorgos Verigakis
_firewall_tags = {
52 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
53 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
54 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
55 efff6193 Giorgos Verigakis
56 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
57 efff6193 Giorgos Verigakis
58 d7ff7f5a Christos Stavrakakis
# Timeout in seconds for building NICs. After this period the NICs considered
59 d7ff7f5a Christos Stavrakakis
# stale and removed from DB.
60 d7ff7f5a Christos Stavrakakis
BUILDING_NIC_TIMEOUT = 180
61 02feca11 Vassilios Karakoidas
62 bfd04b01 Christos Stavrakakis
NIC_FIELDS = ["state", "mac", "ipv4", "ipv6", "network", "firewall_profile",
63 bfd04b01 Christos Stavrakakis
              "index"]
64 a1baa42b Christos Stavrakakis
65 a1baa42b Christos Stavrakakis
66 41a7fae7 Christos Stavrakakis
def handle_vm_quotas(vm, job_id, job_opcode, job_status, job_fields):
67 41a7fae7 Christos Stavrakakis
    """Handle quotas for updated VirtualMachine.
68 41a7fae7 Christos Stavrakakis

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

76 41a7fae7 Christos Stavrakakis
    """
77 41a7fae7 Christos Stavrakakis
    if job_status not in ["success", "error", "canceled"]:
78 88fd91af Christos Stavrakakis
        return vm
79 41a7fae7 Christos Stavrakakis
80 41a7fae7 Christos Stavrakakis
    # Check successful completion of a job will trigger any quotable change in
81 41a7fae7 Christos Stavrakakis
    # the VM state.
82 41a7fae7 Christos Stavrakakis
    action = utils.get_action_from_opcode(job_opcode, job_fields)
83 88fd91af Christos Stavrakakis
    if action == "BUILD":
84 88fd91af Christos Stavrakakis
        # Quotas for new VMs are automatically accepted by the API
85 88fd91af Christos Stavrakakis
        return vm
86 41a7fae7 Christos Stavrakakis
    commission_info = quotas.get_commission_info(vm, action=action,
87 41a7fae7 Christos Stavrakakis
                                                 action_fields=job_fields)
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 88fd91af Christos Stavrakakis
        if job_status == "success":
96 41a7fae7 Christos Stavrakakis
            quotas.accept_serial(serial)
97 41a7fae7 Christos Stavrakakis
        elif job_status in ["error", "canceled"]:
98 41a7fae7 Christos Stavrakakis
            log.debug("Job %s failed. Rejecting related serial %s", job_id,
99 41a7fae7 Christos Stavrakakis
                      serial)
100 41a7fae7 Christos Stavrakakis
            quotas.reject_serial(serial)
101 41a7fae7 Christos Stavrakakis
        vm.serial = None
102 41a7fae7 Christos Stavrakakis
    elif job_status == "success" and commission_info is not None:
103 41a7fae7 Christos Stavrakakis
        log.debug("Expected job was %s. Processing job %s. Commission for"
104 41a7fae7 Christos Stavrakakis
                  " this job: %s", vm.task_job_id, job_id, commission_info)
105 41a7fae7 Christos Stavrakakis
        # Commission for this change has not been issued, or the issued
106 41a7fae7 Christos Stavrakakis
        # commission was unaware of the current change. Reject all previous
107 41a7fae7 Christos Stavrakakis
        # commissions and create a new one in forced mode!
108 9122ffab Christos Stavrakakis
        commission_name = ("client: dispatcher, resource: %s, ganeti_job: %s"
109 9122ffab Christos Stavrakakis
                           % (vm, job_id))
110 5c8076b6 Christos Stavrakakis
        quotas.handle_resource_commission(vm, action,
111 5c8076b6 Christos Stavrakakis
                                          commission_info=commission_info,
112 5c8076b6 Christos Stavrakakis
                                          commission_name=commission_name,
113 5c8076b6 Christos Stavrakakis
                                          force=True,
114 5c8076b6 Christos Stavrakakis
                                          auto_accept=True)
115 5c8076b6 Christos Stavrakakis
        log.debug("Issued new commission: %s", vm.serial)
116 41a7fae7 Christos Stavrakakis
117 41a7fae7 Christos Stavrakakis
    return vm
118 41a7fae7 Christos Stavrakakis
119 41a7fae7 Christos Stavrakakis
120 093f9c53 Vangelis Koukis
@transaction.commit_on_success
121 ca4d59e3 Christos Stavrakakis
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None,
122 e6fbada1 Christos Stavrakakis
                      job_fields=None):
123 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
124 02feca11 Vassilios Karakoidas

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

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

226 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
227 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
228 ad2d6807 Vangelis Koukis

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

231 a1baa42b Christos Stavrakakis
    """
232 b578d9e7 Christos Stavrakakis
    ganeti_nics = process_ganeti_nics(nics)
233 a1baa42b Christos Stavrakakis
    db_nics = dict([(nic.id, nic) for nic in vm.nics.all()])
234 40d53b77 Christos Stavrakakis
235 40d53b77 Christos Stavrakakis
    # Get X-Lock on backend before getting X-Lock on network IP pools, to
236 40d53b77 Christos Stavrakakis
    # guarantee that no deadlock will occur with Backend allocator.
237 40d53b77 Christos Stavrakakis
    Backend.objects.select_for_update().get(id=vm.backend_id)
238 b578d9e7 Christos Stavrakakis
239 a1baa42b Christos Stavrakakis
    for nic_name in set(db_nics.keys()) | set(ganeti_nics.keys()):
240 a1baa42b Christos Stavrakakis
        db_nic = db_nics.get(nic_name)
241 a1baa42b Christos Stavrakakis
        ganeti_nic = ganeti_nics.get(nic_name)
242 a1baa42b Christos Stavrakakis
        if ganeti_nic is None:
243 a1baa42b Christos Stavrakakis
            # NIC exists in DB but not in Ganeti. If the NIC is in 'building'
244 a1baa42b Christos Stavrakakis
            # state for more than 5 minutes, then we remove the NIC.
245 a1baa42b Christos Stavrakakis
            # TODO: This is dangerous as the job may be stack in the queue, and
246 a1baa42b Christos Stavrakakis
            # releasing the IP may lead to duplicate IP use.
247 a1baa42b Christos Stavrakakis
            if nic.state != "BUILDING" or (nic.state == "BUILDING" and
248 d7ff7f5a Christos Stavrakakis
               etime > nic.created + timedelta(seconds=BUILDING_NIC_TIMEOUT)):
249 a1baa42b Christos Stavrakakis
                release_nic_address(nic)
250 a1baa42b Christos Stavrakakis
                nic.delete()
251 a1baa42b Christos Stavrakakis
            else:
252 a1baa42b Christos Stavrakakis
                log.warning("Ignoring recent building NIC: %s", nic)
253 a1baa42b Christos Stavrakakis
        elif db_nic is None:
254 a1baa42b Christos Stavrakakis
            # NIC exists in Ganeti but not in DB
255 a1baa42b Christos Stavrakakis
            if ganeti_nic["ipv4"]:
256 a1baa42b Christos Stavrakakis
                network = ganeti_nic["network"]
257 a1baa42b Christos Stavrakakis
                network.reserve_address(ganeti_nic["ipv4"])
258 a1baa42b Christos Stavrakakis
            vm.nics.create(**ganeti_nic)
259 a1baa42b Christos Stavrakakis
        elif not nics_are_equal(db_nic, ganeti_nic):
260 a1baa42b Christos Stavrakakis
            # Special case where the IPv4 address has changed, because you
261 a1baa42b Christos Stavrakakis
            # need to release the old IPv4 address and reserve the new one
262 a1baa42b Christos Stavrakakis
            if db_nic.ipv4 != ganeti_nic["ipv4"]:
263 a1baa42b Christos Stavrakakis
                release_nic_address(db_nic)
264 a1baa42b Christos Stavrakakis
                if ganeti_nic["ipv4"]:
265 a1baa42b Christos Stavrakakis
                    ganeti_nic["network"].reserve_address(ganeti_nic["ipv4"])
266 a1baa42b Christos Stavrakakis
267 a1baa42b Christos Stavrakakis
            # Update the NIC in DB with the values from Ganeti NIC
268 a1baa42b Christos Stavrakakis
            [setattr(db_nic, f, ganeti_nic[f]) for f in NIC_FIELDS]
269 a1baa42b Christos Stavrakakis
            db_nic.save()
270 a1baa42b Christos Stavrakakis
271 a1baa42b Christos Stavrakakis
            # Dummy update the network, to work with 'changed-since'
272 a1baa42b Christos Stavrakakis
            db_nic.network.save()
273 b578d9e7 Christos Stavrakakis
274 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
275 b578d9e7 Christos Stavrakakis
    vm.save()
276 b578d9e7 Christos Stavrakakis
277 b578d9e7 Christos Stavrakakis
278 a1baa42b Christos Stavrakakis
def nics_are_equal(db_nic, gnt_nic):
279 a1baa42b Christos Stavrakakis
    for field in NIC_FIELDS:
280 a1baa42b Christos Stavrakakis
        if getattr(db_nic, field) != gnt_nic[field]:
281 a1baa42b Christos Stavrakakis
            return False
282 a1baa42b Christos Stavrakakis
    return True
283 a1baa42b Christos Stavrakakis
284 a1baa42b Christos Stavrakakis
285 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
286 a1baa42b Christos Stavrakakis
    """Process NIC dict from ganeti"""
287 b578d9e7 Christos Stavrakakis
    new_nics = []
288 a1baa42b Christos Stavrakakis
    for index, gnic in enumerate(ganeti_nics):
289 a1baa42b Christos Stavrakakis
        nic_name = gnic.get("name", None)
290 a1baa42b Christos Stavrakakis
        if nic_name is not None:
291 a1baa42b Christos Stavrakakis
            nic_id = utils.id_from_nic_name(nic_name)
292 a1baa42b Christos Stavrakakis
        else:
293 a1baa42b Christos Stavrakakis
            # Put as default value the index. If it is an unknown NIC to
294 a1baa42b Christos Stavrakakis
            # synnefo it will be created automaticaly.
295 a1baa42b Christos Stavrakakis
            nic_id = "unknown-" + str(index)
296 a1baa42b Christos Stavrakakis
        network_name = gnic.get('network', '')
297 a1baa42b Christos Stavrakakis
        network_id = utils.id_from_network_name(network_name)
298 a1baa42b Christos Stavrakakis
        network = Network.objects.get(id=network_id)
299 77f0fa63 Christos Stavrakakis
300 77f0fa63 Christos Stavrakakis
        # Get the new nic info
301 a1baa42b Christos Stavrakakis
        mac = gnic.get('mac')
302 a1baa42b Christos Stavrakakis
        ipv4 = gnic.get('ip')
303 a1baa42b Christos Stavrakakis
        ipv6 = mac2eui64(mac, network.subnet6)\
304 a1baa42b Christos Stavrakakis
            if network.subnet6 is not None else None
305 8d325d4b Christos Stavrakakis
306 a1baa42b Christos Stavrakakis
        firewall = gnic.get('firewall')
307 8d325d4b Christos Stavrakakis
        firewall_profile = _reverse_tags.get(firewall)
308 a1baa42b Christos Stavrakakis
        if not firewall_profile and network.public:
309 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
310 9afeb669 Kostas Papadimitriou
311 a1baa42b Christos Stavrakakis
        nic_info = {
312 a1baa42b Christos Stavrakakis
            'index': index,
313 a1baa42b Christos Stavrakakis
            'network': network,
314 cc92b70f Christos Stavrakakis
            'mac': mac,
315 cc92b70f Christos Stavrakakis
            'ipv4': ipv4,
316 cc92b70f Christos Stavrakakis
            'ipv6': ipv6,
317 939d71dd Christos Stavrakakis
            'firewall_profile': firewall_profile,
318 939d71dd Christos Stavrakakis
            'state': 'ACTIVE'}
319 b578d9e7 Christos Stavrakakis
320 a1baa42b Christos Stavrakakis
        new_nics.append((nic_id, nic_info))
321 a1baa42b Christos Stavrakakis
    return dict(new_nics)
322 a1baa42b Christos Stavrakakis
323 a1baa42b Christos Stavrakakis
324 a1baa42b Christos Stavrakakis
def release_nic_address(nic):
325 a1baa42b Christos Stavrakakis
    """Release the IPv4 address of a NIC.
326 a1baa42b Christos Stavrakakis

327 a1baa42b Christos Stavrakakis
    Check if an instance's NIC has an IPv4 address and release it if it is not
328 cb7b1c23 Christos Stavrakakis
    a Floating IP. If it is as Floating IP, then disassociate the FloatingIP
329 cb7b1c23 Christos Stavrakakis
    from the machine.
330 a1baa42b Christos Stavrakakis

331 a1baa42b Christos Stavrakakis
    """
332 a1baa42b Christos Stavrakakis
333 cb7b1c23 Christos Stavrakakis
    if nic.ipv4:
334 cb7b1c23 Christos Stavrakakis
        if nic.ip_type == "FLOATING":
335 cb7b1c23 Christos Stavrakakis
            FloatingIP.objects.filter(machine=nic.machine_id,
336 cb7b1c23 Christos Stavrakakis
                                      network=nic.network_id,
337 cb7b1c23 Christos Stavrakakis
                                      ipv4=nic.ipv4).update(machine=None)
338 cb7b1c23 Christos Stavrakakis
        else:
339 cb7b1c23 Christos Stavrakakis
            nic.network.release_address(nic.ipv4)
340 bd392934 Christos Stavrakakis
341 bd392934 Christos Stavrakakis
342 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
343 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
344 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
345 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
346 22ee6892 Christos Stavrakakis
347 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
348 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
349 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
350 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
351 22ee6892 Christos Stavrakakis
352 05146623 Christos Stavrakakis
    network = back_network.network
353 05146623 Christos Stavrakakis
354 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
355 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
356 22ee6892 Christos Stavrakakis
    if status == 'success' and state_for_success is not None:
357 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
358 22ee6892 Christos Stavrakakis
359 e6f6627c Christos Stavrakakis
    if status in ('canceled', 'error') and opcode == 'OP_NETWORK_ADD':
360 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
361 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
362 22ee6892 Christos Stavrakakis
363 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
364 198d91c3 Christos Stavrakakis
        network_is_deleted = (status == "success")
365 198d91c3 Christos Stavrakakis
        if network_is_deleted or (status == "error" and not
366 198d91c3 Christos Stavrakakis
                                  network_exists_in_backend(back_network)):
367 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
368 e97288bc Christos Stavrakakis
            back_network.deleted = True
369 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
370 22ee6892 Christos Stavrakakis
371 e97288bc Christos Stavrakakis
    if status == 'success':
372 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
373 22ee6892 Christos Stavrakakis
    back_network.save()
374 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
375 05146623 Christos Stavrakakis
    update_network_state(network)
376 fd2bdbb2 Christos Stavrakakis
377 fd2bdbb2 Christos Stavrakakis
378 2509ce17 Christos Stavrakakis
def update_network_state(network):
379 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
380 cb4eee84 Christos Stavrakakis

381 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
382 99af08a4 Christos Stavrakakis
    backends that network exists.
383 99af08a4 Christos Stavrakakis

384 99af08a4 Christos Stavrakakis
    The state of the network is:
385 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
386 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
387 99af08a4 Christos Stavrakakis

388 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
389 99af08a4 Christos Stavrakakis
    quotas for the network.
390 99af08a4 Christos Stavrakakis

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

499 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
500 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
501 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
502 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
503 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
504 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
505 7d43565f Kostas Papadimitriou
    """
506 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
507 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
508 cc92b70f Christos Stavrakakis
                                                   message=message,
509 cc92b70f Christos Stavrakakis
                                                   details=details)
510 7d43565f Kostas Papadimitriou
511 7d43565f Kostas Papadimitriou
512 2c022086 Christos Stavrakakis
def create_instance(vm, nics, flavor, image):
513 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
514 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
515 7f691719 Christos Stavrakakis

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

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

904 1a894bfe Christos Stavrakakis
    """
905 e77a29ab Christos Stavrakakis
    nodes = get_nodes(backend, bulk=True)
906 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
907 1a894bfe Christos Stavrakakis
    res = {}
908 1a894bfe Christos Stavrakakis
    for a in attr:
909 1a894bfe Christos Stavrakakis
        res[a] = 0
910 1a894bfe Christos Stavrakakis
    for n in nodes:
911 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
912 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
913 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
914 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
915 1a894bfe Christos Stavrakakis
            for a in attr:
916 1a894bfe Christos Stavrakakis
                res[a] += int(n[a])
917 1a894bfe Christos Stavrakakis
    return res
918 1a894bfe Christos Stavrakakis
919 1a894bfe Christos Stavrakakis
920 1da50fe3 Christos Stavrakakis
def update_backend_resources(backend, resources=None):
921 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
922 1a894bfe Christos Stavrakakis

923 1a894bfe Christos Stavrakakis
    """
924 17852fe9 Giorgos Verigakis
925 1a894bfe Christos Stavrakakis
    if not resources:
926 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
927 41303ed0 Vangelis Koukis
928 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
929 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
930 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
931 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
932 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
933 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
934 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
935 1a894bfe Christos Stavrakakis
    backend.save()
936 1a894bfe Christos Stavrakakis
937 1a894bfe Christos Stavrakakis
938 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
939 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
940 1a894bfe Christos Stavrakakis

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

944 1a894bfe Christos Stavrakakis
    """
945 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
946 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
947 1a894bfe Christos Stavrakakis
    mem = 0
948 1a894bfe Christos Stavrakakis
    for i in instances:
949 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
950 1a894bfe Christos Stavrakakis
    return mem
951 b3d28af2 Christos Stavrakakis
952 1da50fe3 Christos Stavrakakis
953 1da50fe3 Christos Stavrakakis
def get_available_disk_templates(backend):
954 1da50fe3 Christos Stavrakakis
    """Get the list of available disk templates of a Ganeti backend.
955 1da50fe3 Christos Stavrakakis

956 1da50fe3 Christos Stavrakakis
    The list contains the disk templates that are enabled in the Ganeti backend
957 1da50fe3 Christos Stavrakakis
    and also included in ipolicy-disk-templates.
958 1da50fe3 Christos Stavrakakis

959 1da50fe3 Christos Stavrakakis
    """
960 1da50fe3 Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
961 1da50fe3 Christos Stavrakakis
        info = c.GetInfo()
962 1da50fe3 Christos Stavrakakis
    ipolicy_disk_templates = info["ipolicy"]["disk-templates"]
963 a8ae6989 Christos Stavrakakis
    try:
964 a8ae6989 Christos Stavrakakis
        enabled_disk_templates = info["enabled_disk_templates"]
965 a8ae6989 Christos Stavrakakis
        return [dp for dp in enabled_disk_templates
966 a8ae6989 Christos Stavrakakis
                if dp in ipolicy_disk_templates]
967 a8ae6989 Christos Stavrakakis
    except KeyError:
968 a8ae6989 Christos Stavrakakis
        # Ganeti < 2.8 does not have 'enabled_disk_templates'
969 a8ae6989 Christos Stavrakakis
        return ipolicy_disk_templates
970 1da50fe3 Christos Stavrakakis
971 1da50fe3 Christos Stavrakakis
972 1da50fe3 Christos Stavrakakis
def update_backend_disk_templates(backend):
973 1da50fe3 Christos Stavrakakis
    disk_templates = get_available_disk_templates(backend)
974 1da50fe3 Christos Stavrakakis
    backend.disk_templates = disk_templates
975 1da50fe3 Christos Stavrakakis
    backend.save()
976 1da50fe3 Christos Stavrakakis
977 1da50fe3 Christos Stavrakakis
978 b3d28af2 Christos Stavrakakis
##
979 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
980 b3d28af2 Christos Stavrakakis
##
981 b3d28af2 Christos Stavrakakis
982 b3d28af2 Christos Stavrakakis
983 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
984 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
985 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
986 b3d28af2 Christos Stavrakakis
        return result
987 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
988 b3d28af2 Christos Stavrakakis
    return result
989 b3d28af2 Christos Stavrakakis
990 b3d28af2 Christos Stavrakakis
991 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
992 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
993 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
994 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
995 3524241a Christos Stavrakakis
    return result
996 b3d28af2 Christos Stavrakakis
997 b3d28af2 Christos Stavrakakis
998 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
999 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1000 3524241a Christos Stavrakakis
        for group in client.GetGroups():
1001 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
1002 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
1003 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
1004 3524241a Christos Stavrakakis
            if result[0] != 'success':
1005 3524241a Christos Stavrakakis
                return result
1006 b3d28af2 Christos Stavrakakis
1007 b3d28af2 Christos Stavrakakis
    return result
1008 b3d28af2 Christos Stavrakakis
1009 b3d28af2 Christos Stavrakakis
1010 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
1011 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
1012 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
1013 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
1014 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
1015 cc92b70f Christos Stavrakakis
                                         [result], None)
1016 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
1017 b3d28af2 Christos Stavrakakis
1018 b3d28af2 Christos Stavrakakis
    if status == 'success':
1019 b3d28af2 Christos Stavrakakis
        return (status, None)
1020 b3d28af2 Christos Stavrakakis
    else:
1021 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
1022 b3d28af2 Christos Stavrakakis
        return (status, error)