Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (38.3 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 8764d304 Christos Stavrakakis
                               Flavor, IPAddress)
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 8764d304 Christos Stavrakakis
NIC_FIELDS = ["state", "mac", "ipv4_address", "ipv6_address", "network",
63 8764d304 Christos Stavrakakis
              "firewall_profile", "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 8764d304 Christos Stavrakakis
            ipaddress = None
263 8764d304 Christos Stavrakakis
            network = ganeti_nic["network"]
264 8764d304 Christos Stavrakakis
            ipv4_address = ganeti_nic["ipv4_address"]
265 8764d304 Christos Stavrakakis
            if ipv4_address:
266 8764d304 Christos Stavrakakis
                network.reserve_address(ipv4_address)
267 8764d304 Christos Stavrakakis
                subnet = network.subnets.get(ipversion=4)
268 8764d304 Christos Stavrakakis
                ipaddress = IPAddress.objects.create(address=ipv4_address,
269 8764d304 Christos Stavrakakis
                                                     network=network,
270 8764d304 Christos Stavrakakis
                                                     subnet=subnet,
271 8764d304 Christos Stavrakakis
                                                     userid=vm.userid)
272 8764d304 Christos Stavrakakis
            # TODO
273 8764d304 Christos Stavrakakis
            ganeti_nic.pop("ipv4_address")
274 8764d304 Christos Stavrakakis
            ganeti_nic.pop("ip", None)
275 8764d304 Christos Stavrakakis
            ganeti_nic.pop("ipv6_address")
276 8764d304 Christos Stavrakakis
            nic = vm.nics.create(id=nic_name, **ganeti_nic)
277 8764d304 Christos Stavrakakis
            if ipaddress is not None:
278 8764d304 Christos Stavrakakis
                ipaddress.nic = nic
279 8764d304 Christos Stavrakakis
                ipaddress.save()
280 a1baa42b Christos Stavrakakis
        elif not nics_are_equal(db_nic, ganeti_nic):
281 a1baa42b Christos Stavrakakis
            # Special case where the IPv4 address has changed, because you
282 a1baa42b Christos Stavrakakis
            # need to release the old IPv4 address and reserve the new one
283 8764d304 Christos Stavrakakis
            ipv4_address = ganeti_nic["ipv4_address"]
284 8764d304 Christos Stavrakakis
            if db_nic.ipv4_address != ipv4_address:
285 a1baa42b Christos Stavrakakis
                release_nic_address(db_nic)
286 8764d304 Christos Stavrakakis
                if ipv4_address:
287 8764d304 Christos Stavrakakis
                    network = ganeti_nic["network"]
288 8764d304 Christos Stavrakakis
                    network.reserve_address(ipv4_address)
289 8764d304 Christos Stavrakakis
                    subnet = network.subnets.get(ipversion=4)
290 8764d304 Christos Stavrakakis
                    ipaddress, _ =\
291 8764d304 Christos Stavrakakis
                        IPAddress.objects.get_or_create(network=network,
292 8764d304 Christos Stavrakakis
                                                        subnet=subnet,
293 8764d304 Christos Stavrakakis
                                                        userid=vm.userid,
294 8764d304 Christos Stavrakakis
                                                        address=ipv4_address)
295 8764d304 Christos Stavrakakis
                    ipaddress.nic = nic
296 8764d304 Christos Stavrakakis
                    ipaddress.save()
297 8764d304 Christos Stavrakakis
298 8764d304 Christos Stavrakakis
            for f in ["state", "mac", "network", "firewall_profile", "index"]:
299 8764d304 Christos Stavrakakis
                # Update the NIC in DB with the values from Ganeti NIC
300 8764d304 Christos Stavrakakis
                setattr(db_nic, f, ganeti_nic[f])
301 8764d304 Christos Stavrakakis
                db_nic.save()
302 a1baa42b Christos Stavrakakis
303 a1baa42b Christos Stavrakakis
            # Dummy update the network, to work with 'changed-since'
304 a1baa42b Christos Stavrakakis
            db_nic.network.save()
305 b578d9e7 Christos Stavrakakis
306 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
307 b578d9e7 Christos Stavrakakis
    vm.save()
308 b578d9e7 Christos Stavrakakis
309 b578d9e7 Christos Stavrakakis
310 a1baa42b Christos Stavrakakis
def nics_are_equal(db_nic, gnt_nic):
311 a1baa42b Christos Stavrakakis
    for field in NIC_FIELDS:
312 a1baa42b Christos Stavrakakis
        if getattr(db_nic, field) != gnt_nic[field]:
313 a1baa42b Christos Stavrakakis
            return False
314 a1baa42b Christos Stavrakakis
    return True
315 a1baa42b Christos Stavrakakis
316 a1baa42b Christos Stavrakakis
317 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
318 a1baa42b Christos Stavrakakis
    """Process NIC dict from ganeti"""
319 b578d9e7 Christos Stavrakakis
    new_nics = []
320 a1baa42b Christos Stavrakakis
    for index, gnic in enumerate(ganeti_nics):
321 a1baa42b Christos Stavrakakis
        nic_name = gnic.get("name", None)
322 a1baa42b Christos Stavrakakis
        if nic_name is not None:
323 a1baa42b Christos Stavrakakis
            nic_id = utils.id_from_nic_name(nic_name)
324 a1baa42b Christos Stavrakakis
        else:
325 a1baa42b Christos Stavrakakis
            # Put as default value the index. If it is an unknown NIC to
326 a1baa42b Christos Stavrakakis
            # synnefo it will be created automaticaly.
327 0d069390 Christos Stavrakakis
            nic_id = UNKNOWN_NIC_PREFIX + str(index)
328 a1baa42b Christos Stavrakakis
        network_name = gnic.get('network', '')
329 a1baa42b Christos Stavrakakis
        network_id = utils.id_from_network_name(network_name)
330 a1baa42b Christos Stavrakakis
        network = Network.objects.get(id=network_id)
331 77f0fa63 Christos Stavrakakis
332 77f0fa63 Christos Stavrakakis
        # Get the new nic info
333 a1baa42b Christos Stavrakakis
        mac = gnic.get('mac')
334 a1baa42b Christos Stavrakakis
        ipv4 = gnic.get('ip')
335 8764d304 Christos Stavrakakis
        subnet6 = network.subnet6
336 8764d304 Christos Stavrakakis
        ipv6 = mac2eui64(mac, subnet6) if subnet6 else None
337 8d325d4b Christos Stavrakakis
338 a1baa42b Christos Stavrakakis
        firewall = gnic.get('firewall')
339 8d325d4b Christos Stavrakakis
        firewall_profile = _reverse_tags.get(firewall)
340 a1baa42b Christos Stavrakakis
        if not firewall_profile and network.public:
341 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
342 9afeb669 Kostas Papadimitriou
343 a1baa42b Christos Stavrakakis
        nic_info = {
344 a1baa42b Christos Stavrakakis
            'index': index,
345 a1baa42b Christos Stavrakakis
            'network': network,
346 cc92b70f Christos Stavrakakis
            'mac': mac,
347 8764d304 Christos Stavrakakis
            'ipv4_address': ipv4,
348 8764d304 Christos Stavrakakis
            'ipv6_address': ipv6,
349 939d71dd Christos Stavrakakis
            'firewall_profile': firewall_profile,
350 939d71dd Christos Stavrakakis
            'state': 'ACTIVE'}
351 b578d9e7 Christos Stavrakakis
352 a1baa42b Christos Stavrakakis
        new_nics.append((nic_id, nic_info))
353 a1baa42b Christos Stavrakakis
    return dict(new_nics)
354 a1baa42b Christos Stavrakakis
355 a1baa42b Christos Stavrakakis
356 a1baa42b Christos Stavrakakis
def release_nic_address(nic):
357 a1baa42b Christos Stavrakakis
    """Release the IPv4 address of a NIC.
358 a1baa42b Christos Stavrakakis

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

363 a1baa42b Christos Stavrakakis
    """
364 a1baa42b Christos Stavrakakis
365 8764d304 Christos Stavrakakis
    for ip in nic.ips.all():
366 8764d304 Christos Stavrakakis
        if ip.subnet.ipversion == 4:
367 8764d304 Christos Stavrakakis
            if ip.floating_ip:
368 8764d304 Christos Stavrakakis
                ip.nic = None
369 8764d304 Christos Stavrakakis
                ip.save()
370 8764d304 Christos Stavrakakis
            else:
371 8764d304 Christos Stavrakakis
                ip.network.release_address(ip.address)
372 8764d304 Christos Stavrakakis
                ip.delete()
373 cb7b1c23 Christos Stavrakakis
        else:
374 8764d304 Christos Stavrakakis
            ip.delete()
375 bd392934 Christos Stavrakakis
376 bd392934 Christos Stavrakakis
377 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
378 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
379 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
380 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
381 22ee6892 Christos Stavrakakis
382 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
383 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
384 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
385 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
386 22ee6892 Christos Stavrakakis
387 05146623 Christos Stavrakakis
    network = back_network.network
388 05146623 Christos Stavrakakis
389 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
390 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
391 22ee6892 Christos Stavrakakis
    if status == 'success' and state_for_success is not None:
392 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
393 22ee6892 Christos Stavrakakis
394 e6f6627c Christos Stavrakakis
    if status in ('canceled', 'error') and opcode == 'OP_NETWORK_ADD':
395 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
396 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
397 22ee6892 Christos Stavrakakis
398 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
399 198d91c3 Christos Stavrakakis
        network_is_deleted = (status == "success")
400 198d91c3 Christos Stavrakakis
        if network_is_deleted or (status == "error" and not
401 198d91c3 Christos Stavrakakis
                                  network_exists_in_backend(back_network)):
402 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
403 e97288bc Christos Stavrakakis
            back_network.deleted = True
404 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
405 22ee6892 Christos Stavrakakis
406 e97288bc Christos Stavrakakis
    if status == 'success':
407 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
408 22ee6892 Christos Stavrakakis
    back_network.save()
409 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
410 05146623 Christos Stavrakakis
    update_network_state(network)
411 fd2bdbb2 Christos Stavrakakis
412 fd2bdbb2 Christos Stavrakakis
413 2509ce17 Christos Stavrakakis
def update_network_state(network):
414 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
415 cb4eee84 Christos Stavrakakis

416 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
417 99af08a4 Christos Stavrakakis
    backends that network exists.
418 99af08a4 Christos Stavrakakis

419 99af08a4 Christos Stavrakakis
    The state of the network is:
420 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
421 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
422 99af08a4 Christos Stavrakakis

423 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
424 99af08a4 Christos Stavrakakis
    quotas for the network.
425 99af08a4 Christos Stavrakakis

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

540 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
541 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
542 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
543 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
544 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
545 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
546 7d43565f Kostas Papadimitriou
    """
547 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
548 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
549 cc92b70f Christos Stavrakakis
                                                   message=message,
550 cc92b70f Christos Stavrakakis
                                                   details=details)
551 7d43565f Kostas Papadimitriou
552 7d43565f Kostas Papadimitriou
553 2c022086 Christos Stavrakakis
def create_instance(vm, nics, flavor, image):
554 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
555 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
556 7f691719 Christos Stavrakakis

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

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

950 1a894bfe Christos Stavrakakis
    """
951 e77a29ab Christos Stavrakakis
    nodes = get_nodes(backend, bulk=True)
952 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
953 1a894bfe Christos Stavrakakis
    res = {}
954 1a894bfe Christos Stavrakakis
    for a in attr:
955 1a894bfe Christos Stavrakakis
        res[a] = 0
956 1a894bfe Christos Stavrakakis
    for n in nodes:
957 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
958 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
959 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
960 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
961 1a894bfe Christos Stavrakakis
            for a in attr:
962 121b8921 Christos Stavrakakis
                res[a] += int(n[a] or 0)
963 1a894bfe Christos Stavrakakis
    return res
964 1a894bfe Christos Stavrakakis
965 1a894bfe Christos Stavrakakis
966 1da50fe3 Christos Stavrakakis
def update_backend_resources(backend, resources=None):
967 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
968 1a894bfe Christos Stavrakakis

969 1a894bfe Christos Stavrakakis
    """
970 17852fe9 Giorgos Verigakis
971 1a894bfe Christos Stavrakakis
    if not resources:
972 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
973 41303ed0 Vangelis Koukis
974 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
975 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
976 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
977 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
978 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
979 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
980 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
981 1a894bfe Christos Stavrakakis
    backend.save()
982 1a894bfe Christos Stavrakakis
983 1a894bfe Christos Stavrakakis
984 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
985 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
986 1a894bfe Christos Stavrakakis

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

990 1a894bfe Christos Stavrakakis
    """
991 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
992 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
993 1a894bfe Christos Stavrakakis
    mem = 0
994 1a894bfe Christos Stavrakakis
    for i in instances:
995 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
996 1a894bfe Christos Stavrakakis
    return mem
997 b3d28af2 Christos Stavrakakis
998 1da50fe3 Christos Stavrakakis
999 1da50fe3 Christos Stavrakakis
def get_available_disk_templates(backend):
1000 1da50fe3 Christos Stavrakakis
    """Get the list of available disk templates of a Ganeti backend.
1001 1da50fe3 Christos Stavrakakis

1002 1da50fe3 Christos Stavrakakis
    The list contains the disk templates that are enabled in the Ganeti backend
1003 1da50fe3 Christos Stavrakakis
    and also included in ipolicy-disk-templates.
1004 1da50fe3 Christos Stavrakakis

1005 1da50fe3 Christos Stavrakakis
    """
1006 1da50fe3 Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
1007 1da50fe3 Christos Stavrakakis
        info = c.GetInfo()
1008 1da50fe3 Christos Stavrakakis
    ipolicy_disk_templates = info["ipolicy"]["disk-templates"]
1009 a8ae6989 Christos Stavrakakis
    try:
1010 a8ae6989 Christos Stavrakakis
        enabled_disk_templates = info["enabled_disk_templates"]
1011 a8ae6989 Christos Stavrakakis
        return [dp for dp in enabled_disk_templates
1012 a8ae6989 Christos Stavrakakis
                if dp in ipolicy_disk_templates]
1013 a8ae6989 Christos Stavrakakis
    except KeyError:
1014 a8ae6989 Christos Stavrakakis
        # Ganeti < 2.8 does not have 'enabled_disk_templates'
1015 a8ae6989 Christos Stavrakakis
        return ipolicy_disk_templates
1016 1da50fe3 Christos Stavrakakis
1017 1da50fe3 Christos Stavrakakis
1018 1da50fe3 Christos Stavrakakis
def update_backend_disk_templates(backend):
1019 1da50fe3 Christos Stavrakakis
    disk_templates = get_available_disk_templates(backend)
1020 1da50fe3 Christos Stavrakakis
    backend.disk_templates = disk_templates
1021 1da50fe3 Christos Stavrakakis
    backend.save()
1022 1da50fe3 Christos Stavrakakis
1023 1da50fe3 Christos Stavrakakis
1024 b3d28af2 Christos Stavrakakis
##
1025 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
1026 b3d28af2 Christos Stavrakakis
##
1027 b3d28af2 Christos Stavrakakis
1028 b3d28af2 Christos Stavrakakis
1029 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
1030 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
1031 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
1032 b3d28af2 Christos Stavrakakis
        return result
1033 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
1034 b3d28af2 Christos Stavrakakis
    return result
1035 b3d28af2 Christos Stavrakakis
1036 b3d28af2 Christos Stavrakakis
1037 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
1038 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1039 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
1040 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
1041 3524241a Christos Stavrakakis
    return result
1042 b3d28af2 Christos Stavrakakis
1043 b3d28af2 Christos Stavrakakis
1044 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
1045 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1046 3524241a Christos Stavrakakis
        for group in client.GetGroups():
1047 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
1048 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
1049 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
1050 3524241a Christos Stavrakakis
            if result[0] != 'success':
1051 3524241a Christos Stavrakakis
                return result
1052 b3d28af2 Christos Stavrakakis
1053 b3d28af2 Christos Stavrakakis
    return result
1054 b3d28af2 Christos Stavrakakis
1055 b3d28af2 Christos Stavrakakis
1056 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
1057 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
1058 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
1059 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
1060 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
1061 cc92b70f Christos Stavrakakis
                                         [result], None)
1062 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
1063 b3d28af2 Christos Stavrakakis
1064 b3d28af2 Christos Stavrakakis
    if status == 'success':
1065 b3d28af2 Christos Stavrakakis
        return (status, None)
1066 b3d28af2 Christos Stavrakakis
    else:
1067 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
1068 b3d28af2 Christos Stavrakakis
        return (status, error)