Statistics
| Branch: | Tag: | Revision:

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

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 0d069390 Christos Stavrakakis
BUILDING_NIC_TIMEOUT = timedelta(seconds=180)
61 02feca11 Vassilios Karakoidas
62 bfd04b01 Christos Stavrakakis
NIC_FIELDS = ["state", "mac", "ipv4", "ipv6", "network", "firewall_profile",
63 bfd04b01 Christos Stavrakakis
              "index"]
64 0d069390 Christos Stavrakakis
UNKNOWN_NIC_PREFIX = "unknown-"
65 a1baa42b Christos Stavrakakis
66 a1baa42b Christos Stavrakakis
67 41a7fae7 Christos Stavrakakis
def handle_vm_quotas(vm, job_id, job_opcode, job_status, job_fields):
68 41a7fae7 Christos Stavrakakis
    """Handle quotas for updated VirtualMachine.
69 41a7fae7 Christos Stavrakakis

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

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

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

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

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

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

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

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

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

388 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
389 99af08a4 Christos Stavrakakis
    backends that network exists.
390 99af08a4 Christos Stavrakakis

391 99af08a4 Christos Stavrakakis
    The state of the network is:
392 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
393 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
394 99af08a4 Christos Stavrakakis

395 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
396 99af08a4 Christos Stavrakakis
    quotas for the network.
397 99af08a4 Christos Stavrakakis

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

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

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

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

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

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

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

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

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

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