Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (40.6 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 bfb3f9c2 Christos Stavrakakis
                               Flavor, IPAddress, IPAddressLog)
41 0292883e Christos Stavrakakis
from synnefo.logic import utils, ips
42 cb4eee84 Christos Stavrakakis
from synnefo import quotas
43 0292883e Christos Stavrakakis
from synnefo.api.util import release_resource
44 fd95834e Christos Stavrakakis
from synnefo.util.mac2eui64 import mac2eui64
45 d2036274 Christos Stavrakakis
from synnefo.logic import rapi
46 529178b1 Giorgos Verigakis
47 3524241a Christos Stavrakakis
from logging import getLogger
48 3524241a Christos Stavrakakis
log = getLogger(__name__)
49 9e98ba3c Giorgos Verigakis
50 529178b1 Giorgos Verigakis
51 efff6193 Giorgos Verigakis
_firewall_tags = {
52 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
53 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
54 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
55 efff6193 Giorgos Verigakis
56 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
57 efff6193 Giorgos Verigakis
58 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 3278725f Christos Stavrakakis
SIMPLE_NIC_FIELDS = ["state", "mac", "network", "firewall_profile", "index"]
63 3278725f Christos Stavrakakis
COMPLEX_NIC_FIELDS = ["ipv4_address", "ipv6_address"]
64 3278725f Christos Stavrakakis
NIC_FIELDS = SIMPLE_NIC_FIELDS + COMPLEX_NIC_FIELDS
65 0d069390 Christos Stavrakakis
UNKNOWN_NIC_PREFIX = "unknown-"
66 a1baa42b Christos Stavrakakis
67 a1baa42b Christos Stavrakakis
68 41a7fae7 Christos Stavrakakis
def handle_vm_quotas(vm, job_id, job_opcode, job_status, job_fields):
69 41a7fae7 Christos Stavrakakis
    """Handle quotas for updated VirtualMachine.
70 41a7fae7 Christos Stavrakakis

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

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

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

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

231 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
232 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
233 ad2d6807 Vangelis Koukis

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

236 a1baa42b Christos Stavrakakis
    """
237 b578d9e7 Christos Stavrakakis
    ganeti_nics = process_ganeti_nics(nics)
238 3278725f Christos Stavrakakis
    db_nics = dict([(nic.id, nic)
239 3278725f Christos Stavrakakis
                    for nic in vm.nics.prefetch_related("ips__subnet")])
240 40d53b77 Christos Stavrakakis
241 40d53b77 Christos Stavrakakis
    # Get X-Lock on backend before getting X-Lock on network IP pools, to
242 40d53b77 Christos Stavrakakis
    # guarantee that no deadlock will occur with Backend allocator.
243 40d53b77 Christos Stavrakakis
    Backend.objects.select_for_update().get(id=vm.backend_id)
244 b578d9e7 Christos Stavrakakis
245 a1baa42b Christos Stavrakakis
    for nic_name in set(db_nics.keys()) | set(ganeti_nics.keys()):
246 a1baa42b Christos Stavrakakis
        db_nic = db_nics.get(nic_name)
247 a1baa42b Christos Stavrakakis
        ganeti_nic = ganeti_nics.get(nic_name)
248 a1baa42b Christos Stavrakakis
        if ganeti_nic is None:
249 a1baa42b Christos Stavrakakis
            # NIC exists in DB but not in Ganeti. If the NIC is in 'building'
250 a1baa42b Christos Stavrakakis
            # state for more than 5 minutes, then we remove the NIC.
251 a1baa42b Christos Stavrakakis
            # TODO: This is dangerous as the job may be stack in the queue, and
252 a1baa42b Christos Stavrakakis
            # releasing the IP may lead to duplicate IP use.
253 3a6be177 Christos Stavrakakis
            if db_nic.state != "BUILD" or\
254 3a6be177 Christos Stavrakakis
                (db_nic.state == "BUILD" and
255 0d069390 Christos Stavrakakis
                 etime > db_nic.created + BUILDING_NIC_TIMEOUT):
256 3278725f Christos Stavrakakis
                remove_nic_ips(db_nic)
257 0d069390 Christos Stavrakakis
                db_nic.delete()
258 a1baa42b Christos Stavrakakis
            else:
259 0d069390 Christos Stavrakakis
                log.warning("Ignoring recent building NIC: %s", db_nic)
260 a1baa42b Christos Stavrakakis
        elif db_nic is None:
261 3278725f Christos Stavrakakis
            msg = ("NIC/%s of VM %s does not exist in DB! Cannot automatically"
262 3278725f Christos Stavrakakis
                   " fix this issue!" % (nic_name, vm))
263 3278725f Christos Stavrakakis
            log.error(msg)
264 3278725f Christos Stavrakakis
            continue
265 a1baa42b Christos Stavrakakis
        elif not nics_are_equal(db_nic, ganeti_nic):
266 3278725f Christos Stavrakakis
            for f in SIMPLE_NIC_FIELDS:
267 3278725f Christos Stavrakakis
                # Update the NIC in DB with the values from Ganeti NIC
268 3278725f Christos Stavrakakis
                setattr(db_nic, f, ganeti_nic[f])
269 3278725f Christos Stavrakakis
                db_nic.save()
270 bfb3f9c2 Christos Stavrakakis
271 a1baa42b Christos Stavrakakis
            # Special case where the IPv4 address has changed, because you
272 a1baa42b Christos Stavrakakis
            # need to release the old IPv4 address and reserve the new one
273 8764d304 Christos Stavrakakis
            ipv4_address = ganeti_nic["ipv4_address"]
274 8764d304 Christos Stavrakakis
            if db_nic.ipv4_address != ipv4_address:
275 bfb3f9c2 Christos Stavrakakis
                change_address_of_port(db_nic, vm.userid,
276 bfb3f9c2 Christos Stavrakakis
                                       old_address=db_nic.ipv4_address,
277 bfb3f9c2 Christos Stavrakakis
                                       new_address=ipv4_address,
278 bfb3f9c2 Christos Stavrakakis
                                       version=4)
279 bfb3f9c2 Christos Stavrakakis
280 bfb3f9c2 Christos Stavrakakis
            ipv6_address = ganeti_nic["ipv6_address"]
281 bfb3f9c2 Christos Stavrakakis
            if db_nic.ipv6_address != ipv6_address:
282 bfb3f9c2 Christos Stavrakakis
                change_address_of_port(db_nic, vm.userid,
283 bfb3f9c2 Christos Stavrakakis
                                       old_address=db_nic.ipv6_address,
284 bfb3f9c2 Christos Stavrakakis
                                       new_address=ipv6_address,
285 bfb3f9c2 Christos Stavrakakis
                                       version=6)
286 8764d304 Christos Stavrakakis
287 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
288 b578d9e7 Christos Stavrakakis
    vm.save()
289 b578d9e7 Christos Stavrakakis
290 b578d9e7 Christos Stavrakakis
291 bfb3f9c2 Christos Stavrakakis
def change_address_of_port(port, userid, old_address, new_address, version):
292 bfb3f9c2 Christos Stavrakakis
    """Change."""
293 bfb3f9c2 Christos Stavrakakis
    if old_address is not None:
294 bfb3f9c2 Christos Stavrakakis
        msg = ("IPv%s Address of server '%s' changed from '%s' to '%s'"
295 bfb3f9c2 Christos Stavrakakis
               % (version, port.machine_id, old_address, new_address))
296 bfb3f9c2 Christos Stavrakakis
        log.critical(msg)
297 bfb3f9c2 Christos Stavrakakis
298 bfb3f9c2 Christos Stavrakakis
    # Remove the old IP address
299 bfb3f9c2 Christos Stavrakakis
    remove_nic_ips(port, version=version)
300 bfb3f9c2 Christos Stavrakakis
301 bfb3f9c2 Christos Stavrakakis
    if version == 4:
302 bfb3f9c2 Christos Stavrakakis
        ipaddress = ips.allocate_ip(port.network, userid, address=new_address)
303 bfb3f9c2 Christos Stavrakakis
        ipaddress.nic = port
304 bfb3f9c2 Christos Stavrakakis
        ipaddress.save()
305 bfb3f9c2 Christos Stavrakakis
    elif version == 6:
306 bfb3f9c2 Christos Stavrakakis
        subnet6 = port.network.subnet6
307 bfb3f9c2 Christos Stavrakakis
        ipaddress = IPAddress.objects.create(userid=userid,
308 bfb3f9c2 Christos Stavrakakis
                                             network=port.network,
309 bfb3f9c2 Christos Stavrakakis
                                             subnet=subnet6,
310 bfb3f9c2 Christos Stavrakakis
                                             nic=port,
311 bfb3f9c2 Christos Stavrakakis
                                             address=new_address)
312 bfb3f9c2 Christos Stavrakakis
    else:
313 bfb3f9c2 Christos Stavrakakis
        raise ValueError("Unknown version: %s" % version)
314 bfb3f9c2 Christos Stavrakakis
315 bfb3f9c2 Christos Stavrakakis
    # New address log
316 bfb3f9c2 Christos Stavrakakis
    ip_log = IPAddressLog.objects.create(server_id=port.machine_id,
317 bfb3f9c2 Christos Stavrakakis
                                         network_id=port.network_id,
318 bfb3f9c2 Christos Stavrakakis
                                         address=new_address,
319 bfb3f9c2 Christos Stavrakakis
                                         active=True)
320 bfb3f9c2 Christos Stavrakakis
    log.info("Created IP log entry '%s' for address '%s' to server '%s'",
321 bfb3f9c2 Christos Stavrakakis
             ip_log.id, new_address, port.machine_id)
322 bfb3f9c2 Christos Stavrakakis
323 bfb3f9c2 Christos Stavrakakis
    return ipaddress
324 bfb3f9c2 Christos Stavrakakis
325 bfb3f9c2 Christos Stavrakakis
326 a1baa42b Christos Stavrakakis
def nics_are_equal(db_nic, gnt_nic):
327 a1baa42b Christos Stavrakakis
    for field in NIC_FIELDS:
328 a1baa42b Christos Stavrakakis
        if getattr(db_nic, field) != gnt_nic[field]:
329 a1baa42b Christos Stavrakakis
            return False
330 a1baa42b Christos Stavrakakis
    return True
331 a1baa42b Christos Stavrakakis
332 a1baa42b Christos Stavrakakis
333 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
334 a1baa42b Christos Stavrakakis
    """Process NIC dict from ganeti"""
335 b578d9e7 Christos Stavrakakis
    new_nics = []
336 a1baa42b Christos Stavrakakis
    for index, gnic in enumerate(ganeti_nics):
337 a1baa42b Christos Stavrakakis
        nic_name = gnic.get("name", None)
338 a1baa42b Christos Stavrakakis
        if nic_name is not None:
339 a1baa42b Christos Stavrakakis
            nic_id = utils.id_from_nic_name(nic_name)
340 a1baa42b Christos Stavrakakis
        else:
341 a1baa42b Christos Stavrakakis
            # Put as default value the index. If it is an unknown NIC to
342 a1baa42b Christos Stavrakakis
            # synnefo it will be created automaticaly.
343 0d069390 Christos Stavrakakis
            nic_id = UNKNOWN_NIC_PREFIX + str(index)
344 a1baa42b Christos Stavrakakis
        network_name = gnic.get('network', '')
345 a1baa42b Christos Stavrakakis
        network_id = utils.id_from_network_name(network_name)
346 a1baa42b Christos Stavrakakis
        network = Network.objects.get(id=network_id)
347 77f0fa63 Christos Stavrakakis
348 77f0fa63 Christos Stavrakakis
        # Get the new nic info
349 a1baa42b Christos Stavrakakis
        mac = gnic.get('mac')
350 a1baa42b Christos Stavrakakis
        ipv4 = gnic.get('ip')
351 8764d304 Christos Stavrakakis
        subnet6 = network.subnet6
352 bfb3f9c2 Christos Stavrakakis
        ipv6 = mac2eui64(mac, subnet6.cidr) if subnet6 else None
353 8d325d4b Christos Stavrakakis
354 a1baa42b Christos Stavrakakis
        firewall = gnic.get('firewall')
355 8d325d4b Christos Stavrakakis
        firewall_profile = _reverse_tags.get(firewall)
356 a1baa42b Christos Stavrakakis
        if not firewall_profile and network.public:
357 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
358 9afeb669 Kostas Papadimitriou
359 a1baa42b Christos Stavrakakis
        nic_info = {
360 a1baa42b Christos Stavrakakis
            'index': index,
361 a1baa42b Christos Stavrakakis
            'network': network,
362 cc92b70f Christos Stavrakakis
            'mac': mac,
363 8764d304 Christos Stavrakakis
            'ipv4_address': ipv4,
364 8764d304 Christos Stavrakakis
            'ipv6_address': ipv6,
365 939d71dd Christos Stavrakakis
            'firewall_profile': firewall_profile,
366 939d71dd Christos Stavrakakis
            'state': 'ACTIVE'}
367 b578d9e7 Christos Stavrakakis
368 a1baa42b Christos Stavrakakis
        new_nics.append((nic_id, nic_info))
369 a1baa42b Christos Stavrakakis
    return dict(new_nics)
370 a1baa42b Christos Stavrakakis
371 a1baa42b Christos Stavrakakis
372 bfb3f9c2 Christos Stavrakakis
def remove_nic_ips(nic, version=None):
373 3278725f Christos Stavrakakis
    """Remove IP addresses associated with a NetworkInterface.
374 a1baa42b Christos Stavrakakis

375 3278725f Christos Stavrakakis
    Remove all IP addresses that are associated with the NetworkInterface
376 3278725f Christos Stavrakakis
    object, by returning them to the pool and deleting the IPAddress object. If
377 3278725f Christos Stavrakakis
    the IP is a floating IP, then it is just disassociated from the NIC.
378 bfb3f9c2 Christos Stavrakakis
    If version is specified, then only IP addressses of that version will be
379 bfb3f9c2 Christos Stavrakakis
    removed.
380 a1baa42b Christos Stavrakakis

381 a1baa42b Christos Stavrakakis
    """
382 8764d304 Christos Stavrakakis
    for ip in nic.ips.all():
383 bfb3f9c2 Christos Stavrakakis
        if version and ip.ipversion != version:
384 bfb3f9c2 Christos Stavrakakis
            continue
385 bfb3f9c2 Christos Stavrakakis
386 ff863a80 Christos Stavrakakis
        # Update the DB table holding the logging of all IP addresses
387 bfb3f9c2 Christos Stavrakakis
        terminate_active_ipaddress_log(nic, ip)
388 ff863a80 Christos Stavrakakis
389 bfb3f9c2 Christos Stavrakakis
        if ip.floating_ip:
390 bfb3f9c2 Christos Stavrakakis
            ip.nic = None
391 bfb3f9c2 Christos Stavrakakis
            ip.save()
392 bfb3f9c2 Christos Stavrakakis
        else:
393 bfb3f9c2 Christos Stavrakakis
            # Release the IPv4 address
394 bfb3f9c2 Christos Stavrakakis
            ip.release_address()
395 8764d304 Christos Stavrakakis
            ip.delete()
396 bd392934 Christos Stavrakakis
397 bd392934 Christos Stavrakakis
398 bfb3f9c2 Christos Stavrakakis
def terminate_active_ipaddress_log(nic, ip):
399 ff863a80 Christos Stavrakakis
    """Update DB logging entry for this IP address."""
400 fae6e5f0 Christos Stavrakakis
    if not ip.network.public or nic.machine is None:
401 ff863a80 Christos Stavrakakis
        return
402 ff863a80 Christos Stavrakakis
    try:
403 ff863a80 Christos Stavrakakis
        ip_log, created = \
404 ff863a80 Christos Stavrakakis
            IPAddressLog.objects.get_or_create(server_id=nic.machine_id,
405 ff863a80 Christos Stavrakakis
                                               network_id=ip.network_id,
406 ff863a80 Christos Stavrakakis
                                               address=ip.address,
407 ff863a80 Christos Stavrakakis
                                               active=True)
408 ff863a80 Christos Stavrakakis
    except IPAddressLog.MultipleObjectsReturned:
409 ff863a80 Christos Stavrakakis
        logmsg = ("Multiple active log entries for IP %s, Network %s,"
410 8d5795b4 Christos Stavrakakis
                  "Server %s. Cannot proceed!"
411 ff863a80 Christos Stavrakakis
                  % (ip.address, ip.network, nic.machine))
412 ff863a80 Christos Stavrakakis
        log.error(logmsg)
413 ff863a80 Christos Stavrakakis
        raise
414 ff863a80 Christos Stavrakakis
415 ff863a80 Christos Stavrakakis
    if created:
416 ff863a80 Christos Stavrakakis
        logmsg = ("No log entry for IP %s, Network %s, Server %s. Created new"
417 ff863a80 Christos Stavrakakis
                  " but with wrong creation timestamp."
418 ff863a80 Christos Stavrakakis
                  % (ip.address, ip.network, nic.machine))
419 ff863a80 Christos Stavrakakis
        log.error(logmsg)
420 ff863a80 Christos Stavrakakis
    ip_log.released_at = datetime.now()
421 ff863a80 Christos Stavrakakis
    ip_log.active = False
422 ff863a80 Christos Stavrakakis
    ip_log.save()
423 ff863a80 Christos Stavrakakis
424 ff863a80 Christos Stavrakakis
425 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
426 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
427 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
428 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
429 22ee6892 Christos Stavrakakis
430 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
431 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
432 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
433 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
434 22ee6892 Christos Stavrakakis
435 c82f57ad Christos Stavrakakis
    # Note: Network is already locked!
436 05146623 Christos Stavrakakis
    network = back_network.network
437 05146623 Christos Stavrakakis
438 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
439 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
440 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS and state_for_success is not None:
441 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
442 22ee6892 Christos Stavrakakis
443 d2036274 Christos Stavrakakis
    if (status in (rapi.JOB_STATUS_CANCELED, rapi.JOB_STATUS_ERROR)
444 d2036274 Christos Stavrakakis
       and opcode == 'OP_NETWORK_ADD'):
445 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
446 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
447 22ee6892 Christos Stavrakakis
448 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
449 d2036274 Christos Stavrakakis
        network_is_deleted = (status == rapi.JOB_STATUS_SUCCESS)
450 d2036274 Christos Stavrakakis
        if network_is_deleted or (status == rapi.JOB_STATUS_ERROR and not
451 198d91c3 Christos Stavrakakis
                                  network_exists_in_backend(back_network)):
452 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
453 e97288bc Christos Stavrakakis
            back_network.deleted = True
454 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
455 22ee6892 Christos Stavrakakis
456 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS:
457 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
458 22ee6892 Christos Stavrakakis
    back_network.save()
459 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
460 05146623 Christos Stavrakakis
    update_network_state(network)
461 fd2bdbb2 Christos Stavrakakis
462 fd2bdbb2 Christos Stavrakakis
463 2509ce17 Christos Stavrakakis
def update_network_state(network):
464 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
465 cb4eee84 Christos Stavrakakis

466 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
467 99af08a4 Christos Stavrakakis
    backends that network exists.
468 99af08a4 Christos Stavrakakis

469 99af08a4 Christos Stavrakakis
    The state of the network is:
470 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
471 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
472 99af08a4 Christos Stavrakakis

473 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
474 99af08a4 Christos Stavrakakis
    quotas for the network.
475 99af08a4 Christos Stavrakakis

476 99af08a4 Christos Stavrakakis
    """
477 99af08a4 Christos Stavrakakis
    if network.deleted:
478 99af08a4 Christos Stavrakakis
        # Network has already been deleted. Just assert that state is also
479 99af08a4 Christos Stavrakakis
        # DELETED
480 99af08a4 Christos Stavrakakis
        if not network.state == "DELETED":
481 99af08a4 Christos Stavrakakis
            network.state = "DELETED"
482 99af08a4 Christos Stavrakakis
            network.save()
483 cb4eee84 Christos Stavrakakis
        return
484 cb4eee84 Christos Stavrakakis
485 99af08a4 Christos Stavrakakis
    backend_states = [s.operstate for s in network.backend_networks.all()]
486 27cda06b Christos Stavrakakis
    if not backend_states and network.action != "DESTROY":
487 99af08a4 Christos Stavrakakis
        if network.state != "ACTIVE":
488 99af08a4 Christos Stavrakakis
            network.state = "ACTIVE"
489 99af08a4 Christos Stavrakakis
            network.save()
490 99af08a4 Christos Stavrakakis
            return
491 99af08a4 Christos Stavrakakis
492 99af08a4 Christos Stavrakakis
    # Network is deleted when all BackendNetworks go to "DELETED" operstate
493 27cda06b Christos Stavrakakis
    deleted = reduce(lambda x, y: x == y and "DELETED", backend_states,
494 27cda06b Christos Stavrakakis
                     "DELETED")
495 99af08a4 Christos Stavrakakis
496 cb4eee84 Christos Stavrakakis
    # Release the resources on the deletion of the Network
497 99af08a4 Christos Stavrakakis
    if deleted:
498 c82f57ad Christos Stavrakakis
        if network.ips.filter(deleted=False, floating_ip=True).exists():
499 8d5795b4 Christos Stavrakakis
            msg = "Cannot delete network %s! Floating IPs still in use!"
500 c82f57ad Christos Stavrakakis
            log.error(msg % network)
501 c82f57ad Christos Stavrakakis
            raise Exception(msg % network)
502 cb4eee84 Christos Stavrakakis
        log.info("Network %r deleted. Releasing link %r mac_prefix %r",
503 cb4eee84 Christos Stavrakakis
                 network.id, network.mac_prefix, network.link)
504 cb4eee84 Christos Stavrakakis
        network.deleted = True
505 99af08a4 Christos Stavrakakis
        network.state = "DELETED"
506 b7d38981 Dimitris Aragiorgis
        if network.mac_prefix:
507 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["mac_prefix"] == "pool":
508 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="mac_prefix",
509 b7d38981 Dimitris Aragiorgis
                                 value=network.mac_prefix)
510 b7d38981 Dimitris Aragiorgis
        if network.link:
511 b7d38981 Dimitris Aragiorgis
            if network.FLAVORS[network.flavor]["link"] == "pool":
512 b7d38981 Dimitris Aragiorgis
                release_resource(res_type="bridge", value=network.link)
513 cb4eee84 Christos Stavrakakis
514 c82f57ad Christos Stavrakakis
        # Set all subnets as deleted
515 c82f57ad Christos Stavrakakis
        network.subnets.update(deleted=True)
516 c82f57ad Christos Stavrakakis
        # And delete the IP pools
517 c82f57ad Christos Stavrakakis
        for subnet in network.subnets.all():
518 c82f57ad Christos Stavrakakis
            if subnet.ipversion == 4:
519 c82f57ad Christos Stavrakakis
                subnet.ip_pools.all().delete()
520 e8234183 Christos Stavrakakis
        # And all the backend networks since there are useless
521 e8234183 Christos Stavrakakis
        network.backend_networks.all().delete()
522 c82f57ad Christos Stavrakakis
523 cb4eee84 Christos Stavrakakis
        # Issue commission
524 e18c1749 Christos Stavrakakis
        if network.userid:
525 2509ce17 Christos Stavrakakis
            quotas.issue_and_accept_commission(network, delete=True)
526 32a0b855 Giorgos Korfiatis
            # the above has already saved the object and committed;
527 32a0b855 Giorgos Korfiatis
            # a second save would override others' changes, since the
528 32a0b855 Giorgos Korfiatis
            # object is now unlocked
529 32a0b855 Giorgos Korfiatis
            return
530 e18c1749 Christos Stavrakakis
        elif not network.public:
531 e18c1749 Christos Stavrakakis
            log.warning("Network %s does not have an owner!", network.id)
532 cb4eee84 Christos Stavrakakis
    network.save()
533 cb4eee84 Christos Stavrakakis
534 cb4eee84 Christos Stavrakakis
535 fd2bdbb2 Christos Stavrakakis
@transaction.commit_on_success
536 fd2bdbb2 Christos Stavrakakis
def process_network_modify(back_network, etime, jobid, opcode, status,
537 e6fbada1 Christos Stavrakakis
                           job_fields):
538 fd2bdbb2 Christos Stavrakakis
    assert (opcode == "OP_NETWORK_SET_PARAMS")
539 fd2bdbb2 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
540 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
541 fd2bdbb2 Christos Stavrakakis
542 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobid = jobid
543 fd2bdbb2 Christos Stavrakakis
    back_network.backendjobstatus = status
544 fd2bdbb2 Christos Stavrakakis
    back_network.opcode = opcode
545 fd2bdbb2 Christos Stavrakakis
546 e6fbada1 Christos Stavrakakis
    add_reserved_ips = job_fields.get("add_reserved_ips")
547 fc56ae0f Christos Stavrakakis
    if add_reserved_ips:
548 3278725f Christos Stavrakakis
        network = back_network.network
549 3278725f Christos Stavrakakis
        for ip in add_reserved_ips:
550 3278725f Christos Stavrakakis
            network.reserve_address(ip, external=True)
551 fd2bdbb2 Christos Stavrakakis
552 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS:
553 fd2bdbb2 Christos Stavrakakis
        back_network.backendtime = etime
554 fd2bdbb2 Christos Stavrakakis
    back_network.save()
555 ad2d6807 Vangelis Koukis
556 c25cc9ec Vangelis Koukis
557 9068cd85 Georgios Gousios
@transaction.commit_on_success
558 0827883e Nikos Skalkotos
def process_create_progress(vm, etime, progress):
559 9068cd85 Georgios Gousios
560 0827883e Nikos Skalkotos
    percentage = int(progress)
561 9068cd85 Georgios Gousios
562 af90d919 Vangelis Koukis
    # The percentage may exceed 100%, due to the way
563 0827883e Nikos Skalkotos
    # snf-image:copy-progress tracks bytes read by image handling processes
564 af90d919 Vangelis Koukis
    percentage = 100 if percentage > 100 else percentage
565 af90d919 Vangelis Koukis
    if percentage < 0:
566 af90d919 Vangelis Koukis
        raise ValueError("Percentage cannot be negative")
567 9068cd85 Georgios Gousios
568 7ec9558b Vangelis Koukis
    # FIXME: log a warning here, see #1033
569 7ec9558b Vangelis Koukis
#   if last_update > percentage:
570 7ec9558b Vangelis Koukis
#       raise ValueError("Build percentage should increase monotonically " \
571 7ec9558b Vangelis Koukis
#                        "(old = %d, new = %d)" % (last_update, percentage))
572 9068cd85 Georgios Gousios
573 c25cc9ec Vangelis Koukis
    # This assumes that no message of type 'ganeti-create-progress' is going to
574 c25cc9ec Vangelis Koukis
    # arrive once OP_INSTANCE_CREATE has succeeded for a Ganeti instance and
575 c25cc9ec Vangelis Koukis
    # the instance is STARTED.  What if the two messages are processed by two
576 c25cc9ec Vangelis Koukis
    # separate dispatcher threads, and the 'ganeti-op-status' message for
577 c25cc9ec Vangelis Koukis
    # successful creation gets processed before the 'ganeti-create-progress'
578 c25cc9ec Vangelis Koukis
    # message? [vkoukis]
579 c25cc9ec Vangelis Koukis
    #
580 c25cc9ec Vangelis Koukis
    #if not vm.operstate == 'BUILD':
581 c25cc9ec Vangelis Koukis
    #    raise VirtualMachine.IllegalState("VM is not in building state")
582 9068cd85 Georgios Gousios
583 c25cc9ec Vangelis Koukis
    vm.buildpercentage = percentage
584 c4e55622 Christos Stavrakakis
    vm.backendtime = etime
585 9068cd85 Georgios Gousios
    vm.save()
586 ad2d6807 Vangelis Koukis
587 c25cc9ec Vangelis Koukis
588 952b2a48 Christos Stavrakakis
@transaction.commit_on_success
589 7d43565f Kostas Papadimitriou
def create_instance_diagnostic(vm, message, source, level="DEBUG", etime=None,
590 cc92b70f Christos Stavrakakis
                               details=None):
591 7d43565f Kostas Papadimitriou
    """
592 7d43565f Kostas Papadimitriou
    Create virtual machine instance diagnostic entry.
593 7d43565f Kostas Papadimitriou

594 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
595 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
596 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
597 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
598 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
599 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
600 7d43565f Kostas Papadimitriou
    """
601 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
602 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
603 cc92b70f Christos Stavrakakis
                                                   message=message,
604 cc92b70f Christos Stavrakakis
                                                   details=details)
605 7d43565f Kostas Papadimitriou
606 7d43565f Kostas Papadimitriou
607 2c022086 Christos Stavrakakis
def create_instance(vm, nics, flavor, image):
608 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
609 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
610 7f691719 Christos Stavrakakis

611 3a9b3cde Giorgos Verigakis
        metadata value should be a dictionary.
612 3a9b3cde Giorgos Verigakis
    """
613 864bed43 Christos Stavrakakis
614 1c382247 Vangelis Koukis
    # Handle arguments to CreateInstance() as a dictionary,
615 1c382247 Vangelis Koukis
    # initialize it based on a deployment-specific value.
616 1c382247 Vangelis Koukis
    # This enables the administrator to override deployment-specific
617 c25cc9ec Vangelis Koukis
    # arguments, such as the disk template to use, name of os provider
618 1c382247 Vangelis Koukis
    # and hypervisor-specific parameters at will (see Synnefo #785, #835).
619 1c382247 Vangelis Koukis
    #
620 bd87213f Christos Stavrakakis
    kw = vm.backend.get_create_params()
621 1c382247 Vangelis Koukis
    kw['mode'] = 'create'
622 924d8085 Christos Stavrakakis
    kw['name'] = vm.backend_vm_id
623 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
624 296682fe Kostas Papadimitriou
625 2a599282 Christos Stavrakakis
    kw['disk_template'] = flavor.disk_template
626 d7841399 Christos Stavrakakis
    kw['disks'] = [{"size": flavor.disk * 1024}]
627 006c6249 Christos Stavrakakis
    provider = flavor.disk_provider
628 296682fe Kostas Papadimitriou
    if provider:
629 296682fe Kostas Papadimitriou
        kw['disks'][0]['provider'] = provider
630 b2222a7f Christos Stavrakakis
        kw['disks'][0]['origin'] = flavor.disk_origin
631 296682fe Kostas Papadimitriou
632 7c714455 Christos Stavrakakis
    kw['nics'] = [{"name": nic.backend_uuid,
633 7c714455 Christos Stavrakakis
                   "network": nic.network.backend_id,
634 92d2d1ce Christos Stavrakakis
                   "ip": nic.ipv4_address}
635 cb66110b Christos Stavrakakis
                  for nic in nics]
636 d2036274 Christos Stavrakakis
637 cb66110b Christos Stavrakakis
    backend = vm.backend
638 cb66110b Christos Stavrakakis
    depend_jobs = []
639 cb66110b Christos Stavrakakis
    for nic in nics:
640 d2036274 Christos Stavrakakis
        bnet, job_ids = ensure_network_is_active(backend, nic.network_id)
641 d2036274 Christos Stavrakakis
        depend_jobs.extend(job_ids)
642 d2036274 Christos Stavrakakis
643 d2036274 Christos Stavrakakis
    kw["depends"] = create_job_dependencies(depend_jobs)
644 cb66110b Christos Stavrakakis
645 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
646 1c382247 Vangelis Koukis
    # kw['os'] = settings.GANETI_OS_PROVIDER
647 1c382247 Vangelis Koukis
    kw['ip_check'] = False
648 1c382247 Vangelis Koukis
    kw['name_check'] = False
649 79b7dbb7 Christos Stavrakakis
650 1c382247 Vangelis Koukis
    # Do not specific a node explicitly, have
651 1c382247 Vangelis Koukis
    # Ganeti use an iallocator instead
652 f8d1eaf4 Kostas Papadimitriou
    #kw['pnode'] = rapi.GetNodes()[0]
653 79b7dbb7 Christos Stavrakakis
654 1c382247 Vangelis Koukis
    kw['dry_run'] = settings.TEST
655 41303ed0 Vangelis Koukis
656 2b1db26f Giorgos Verigakis
    kw['beparams'] = {
657 cc92b70f Christos Stavrakakis
        'auto_balance': True,
658 cc92b70f Christos Stavrakakis
        'vcpus': flavor.cpu,
659 cc92b70f Christos Stavrakakis
        'memory': flavor.ram}
660 79b7dbb7 Christos Stavrakakis
661 e3b5be49 Giorgos Verigakis
    kw['osparams'] = {
662 79b7dbb7 Christos Stavrakakis
        'config_url': vm.config_url,
663 79b7dbb7 Christos Stavrakakis
        # Store image id and format to Ganeti
664 2a599282 Christos Stavrakakis
        'img_id': image['backend_id'],
665 3a9b3cde Giorgos Verigakis
        'img_format': image['format']}
666 d1eaa651 Christos Stavrakakis
667 e9d59f5e Christos Stavrakakis
    # Use opportunistic locking
668 e9d59f5e Christos Stavrakakis
    kw['opportunistic_locking'] = True
669 e9d59f5e Christos Stavrakakis
670 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
671 1c382247 Vangelis Koukis
    # kw['hvparams'] = dict(serial_console=False)
672 79b7dbb7 Christos Stavrakakis
673 6afeb85d Christos Stavrakakis
    log.debug("Creating instance %s", utils.hide_pass(kw))
674 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
675 3524241a Christos Stavrakakis
        return client.CreateInstance(**kw)
676 f533f224 Vangelis Koukis
677 529178b1 Giorgos Verigakis
678 529178b1 Giorgos Verigakis
def delete_instance(vm):
679 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
680 3524241a Christos Stavrakakis
        return client.DeleteInstance(vm.backend_vm_id, dry_run=settings.TEST)
681 529178b1 Giorgos Verigakis
682 ad2d6807 Vangelis Koukis
683 529178b1 Giorgos Verigakis
def reboot_instance(vm, reboot_type):
684 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
685 bbae3e45 Christos Stavrakakis
    kwargs = {"instance": vm.backend_vm_id,
686 bbae3e45 Christos Stavrakakis
              "reboot_type": "hard"}
687 bbae3e45 Christos Stavrakakis
    # XXX: Currently shutdown_timeout parameter is not supported from the
688 bbae3e45 Christos Stavrakakis
    # Ganeti RAPI. Until supported, we will fallback for both reboot types
689 bbae3e45 Christos Stavrakakis
    # to the default shutdown timeout of Ganeti (120s). Note that reboot
690 bbae3e45 Christos Stavrakakis
    # type of Ganeti job must be always hard. The 'soft' and 'hard' type
691 bbae3e45 Christos Stavrakakis
    # of OS API is different from the one in Ganeti, and maps to
692 bbae3e45 Christos Stavrakakis
    # 'shutdown_timeout'.
693 bbae3e45 Christos Stavrakakis
    #if reboot_type == "hard":
694 bbae3e45 Christos Stavrakakis
    #    kwargs["shutdown_timeout"] = 0
695 bbae3e45 Christos Stavrakakis
    if settings.TEST:
696 bbae3e45 Christos Stavrakakis
        kwargs["dry_run"] = True
697 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
698 bbae3e45 Christos Stavrakakis
        return client.RebootInstance(**kwargs)
699 529178b1 Giorgos Verigakis
700 ad2d6807 Vangelis Koukis
701 529178b1 Giorgos Verigakis
def startup_instance(vm):
702 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
703 3524241a Christos Stavrakakis
        return client.StartupInstance(vm.backend_vm_id, dry_run=settings.TEST)
704 529178b1 Giorgos Verigakis
705 ad2d6807 Vangelis Koukis
706 529178b1 Giorgos Verigakis
def shutdown_instance(vm):
707 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
708 3524241a Christos Stavrakakis
        return client.ShutdownInstance(vm.backend_vm_id, dry_run=settings.TEST)
709 529178b1 Giorgos Verigakis
710 ad2d6807 Vangelis Koukis
711 2cd3f389 Christos Stavrakakis
def resize_instance(vm, vcpus, memory):
712 2cd3f389 Christos Stavrakakis
    beparams = {"vcpus": int(vcpus),
713 2cd3f389 Christos Stavrakakis
                "minmem": int(memory),
714 2cd3f389 Christos Stavrakakis
                "maxmem": int(memory)}
715 2cd3f389 Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
716 2cd3f389 Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, beparams=beparams)
717 2cd3f389 Christos Stavrakakis
718 2cd3f389 Christos Stavrakakis
719 529178b1 Giorgos Verigakis
def get_instance_console(vm):
720 71099804 Vangelis Koukis
    # RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
721 71099804 Vangelis Koukis
    # which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
722 71099804 Vangelis Koukis
    # useless (see #783).
723 71099804 Vangelis Koukis
    #
724 71099804 Vangelis Koukis
    # Until this is fixed on the Ganeti side, construct a console info reply
725 71099804 Vangelis Koukis
    # directly.
726 9afeb669 Kostas Papadimitriou
    #
727 71099804 Vangelis Koukis
    # WARNING: This assumes that VNC runs on port network_port on
728 71099804 Vangelis Koukis
    #          the instance's primary node, and is probably
729 71099804 Vangelis Koukis
    #          hypervisor-specific.
730 71099804 Vangelis Koukis
    #
731 bf5c82dc Christos Stavrakakis
    log.debug("Getting console for vm %s", vm)
732 bf5c82dc Christos Stavrakakis
733 71099804 Vangelis Koukis
    console = {}
734 71099804 Vangelis Koukis
    console['kind'] = 'vnc'
735 3524241a Christos Stavrakakis
736 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
737 3524241a Christos Stavrakakis
        i = client.GetInstance(vm.backend_vm_id)
738 3524241a Christos Stavrakakis
739 bd87213f Christos Stavrakakis
    if vm.backend.hypervisor == "kvm" and i['hvparams']['serial_console']:
740 71099804 Vangelis Koukis
        raise Exception("hv parameter serial_console cannot be true")
741 71099804 Vangelis Koukis
    console['host'] = i['pnode']
742 71099804 Vangelis Koukis
    console['port'] = i['network_port']
743 9afeb669 Kostas Papadimitriou
744 71099804 Vangelis Koukis
    return console
745 604b2bf8 Georgios Gousios
746 604b2bf8 Georgios Gousios
747 3524241a Christos Stavrakakis
def get_instance_info(vm):
748 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
749 198d91c3 Christos Stavrakakis
        return client.GetInstance(vm.backend_vm_id)
750 198d91c3 Christos Stavrakakis
751 198d91c3 Christos Stavrakakis
752 198d91c3 Christos Stavrakakis
def vm_exists_in_backend(vm):
753 198d91c3 Christos Stavrakakis
    try:
754 198d91c3 Christos Stavrakakis
        get_instance_info(vm)
755 198d91c3 Christos Stavrakakis
        return True
756 d2036274 Christos Stavrakakis
    except rapi.GanetiApiError as e:
757 198d91c3 Christos Stavrakakis
        if e.code == 404:
758 198d91c3 Christos Stavrakakis
            return False
759 198d91c3 Christos Stavrakakis
        raise e
760 198d91c3 Christos Stavrakakis
761 198d91c3 Christos Stavrakakis
762 198d91c3 Christos Stavrakakis
def get_network_info(backend_network):
763 198d91c3 Christos Stavrakakis
    with pooled_rapi_client(backend_network) as client:
764 198d91c3 Christos Stavrakakis
        return client.GetNetwork(backend_network.network.backend_id)
765 198d91c3 Christos Stavrakakis
766 198d91c3 Christos Stavrakakis
767 198d91c3 Christos Stavrakakis
def network_exists_in_backend(backend_network):
768 198d91c3 Christos Stavrakakis
    try:
769 198d91c3 Christos Stavrakakis
        get_network_info(backend_network)
770 198d91c3 Christos Stavrakakis
        return True
771 d2036274 Christos Stavrakakis
    except rapi.GanetiApiError as e:
772 198d91c3 Christos Stavrakakis
        if e.code == 404:
773 198d91c3 Christos Stavrakakis
            return False
774 f533f224 Vangelis Koukis
775 c25cc9ec Vangelis Koukis
776 d2036274 Christos Stavrakakis
def ensure_network_is_active(backend, network_id):
777 d2036274 Christos Stavrakakis
    """Ensure that a network is active in the specified backend
778 d2036274 Christos Stavrakakis

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

783 d2036274 Christos Stavrakakis
    """
784 d2036274 Christos Stavrakakis
    network = Network.objects.select_for_update().get(id=network_id)
785 d2036274 Christos Stavrakakis
    bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
786 d2036274 Christos Stavrakakis
                                                         network=network)
787 d2036274 Christos Stavrakakis
    job_ids = []
788 d2036274 Christos Stavrakakis
    if bnet.operstate != "ACTIVE":
789 d2036274 Christos Stavrakakis
        job_ids = create_network(network, backend, connect=True)
790 d2036274 Christos Stavrakakis
791 d2036274 Christos Stavrakakis
    return bnet, job_ids
792 d2036274 Christos Stavrakakis
793 d2036274 Christos Stavrakakis
794 99af08a4 Christos Stavrakakis
def create_network(network, backend, connect=True):
795 99af08a4 Christos Stavrakakis
    """Create a network in a Ganeti backend"""
796 99af08a4 Christos Stavrakakis
    log.debug("Creating network %s in backend %s", network, backend)
797 64938cb0 Giorgos Verigakis
798 99af08a4 Christos Stavrakakis
    job_id = _create_network(network, backend)
799 c25cc9ec Vangelis Koukis
800 99af08a4 Christos Stavrakakis
    if connect:
801 99af08a4 Christos Stavrakakis
        job_ids = connect_network(network, backend, depends=[job_id])
802 99af08a4 Christos Stavrakakis
        return job_ids
803 99af08a4 Christos Stavrakakis
    else:
804 99af08a4 Christos Stavrakakis
        return [job_id]
805 37ca953f Christodoulos Psaltis
806 64938cb0 Giorgos Verigakis
807 3524241a Christos Stavrakakis
def _create_network(network, backend):
808 3524241a Christos Stavrakakis
    """Create a network."""
809 c25cc9ec Vangelis Koukis
810 22ee6892 Christos Stavrakakis
    tags = network.backend_tag
811 92d2d1ce Christos Stavrakakis
    subnet = None
812 92d2d1ce Christos Stavrakakis
    subnet6 = None
813 92d2d1ce Christos Stavrakakis
    gateway = None
814 92d2d1ce Christos Stavrakakis
    gateway6 = None
815 3278725f Christos Stavrakakis
    for _subnet in network.subnets.all():
816 a2bd0802 Christos Stavrakakis
        if _subnet.dhcp and not "nfdhcpd" in tags:
817 a2bd0802 Christos Stavrakakis
            tags.append("nfdhcpd")
818 3278725f Christos Stavrakakis
        if _subnet.ipversion == 4:
819 fe3b2809 Christos Stavrakakis
            subnet = _subnet.cidr
820 fe3b2809 Christos Stavrakakis
            gateway = _subnet.gateway
821 3278725f Christos Stavrakakis
        elif _subnet.ipversion == 6:
822 fe3b2809 Christos Stavrakakis
            subnet6 = _subnet.cidr
823 fe3b2809 Christos Stavrakakis
            gateway6 = _subnet.gateway
824 2d762302 Dimitris Aragiorgis
825 2d762302 Dimitris Aragiorgis
    if network.public:
826 2d762302 Dimitris Aragiorgis
        conflicts_check = True
827 700b85be Dimitris Aragiorgis
        tags.append('public')
828 2d762302 Dimitris Aragiorgis
    else:
829 2d762302 Dimitris Aragiorgis
        conflicts_check = False
830 700b85be Dimitris Aragiorgis
        tags.append('private')
831 22ee6892 Christos Stavrakakis
832 5aeb4e93 Christos Stavrakakis
    # Use a dummy network subnet for IPv6 only networks. Currently Ganeti does
833 5aeb4e93 Christos Stavrakakis
    # not support IPv6 only networks. To bypass this limitation, we create the
834 5aeb4e93 Christos Stavrakakis
    # network with a dummy network subnet, and make Cyclades connect instances
835 5aeb4e93 Christos Stavrakakis
    # to such networks, with address=None.
836 5aeb4e93 Christos Stavrakakis
    if subnet is None:
837 bac6ed1e Christos Stavrakakis
        subnet = "10.0.0.0/30"
838 5aeb4e93 Christos Stavrakakis
839 3524241a Christos Stavrakakis
    try:
840 3524241a Christos Stavrakakis
        bn = BackendNetwork.objects.get(network=network, backend=backend)
841 3524241a Christos Stavrakakis
        mac_prefix = bn.mac_prefix
842 3524241a Christos Stavrakakis
    except BackendNetwork.DoesNotExist:
843 cc92b70f Christos Stavrakakis
        raise Exception("BackendNetwork for network '%s' in backend '%s'"
844 3524241a Christos Stavrakakis
                        " does not exist" % (network.id, backend.id))
845 3524241a Christos Stavrakakis
846 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
847 3524241a Christos Stavrakakis
        return client.CreateNetwork(network_name=network.backend_id,
848 5aeb4e93 Christos Stavrakakis
                                    network=subnet,
849 92d2d1ce Christos Stavrakakis
                                    network6=subnet6,
850 92d2d1ce Christos Stavrakakis
                                    gateway=gateway,
851 92d2d1ce Christos Stavrakakis
                                    gateway6=gateway6,
852 3524241a Christos Stavrakakis
                                    mac_prefix=mac_prefix,
853 2d762302 Dimitris Aragiorgis
                                    conflicts_check=conflicts_check,
854 3524241a Christos Stavrakakis
                                    tags=tags)
855 3524241a Christos Stavrakakis
856 3524241a Christos Stavrakakis
857 99af08a4 Christos Stavrakakis
def connect_network(network, backend, depends=[], group=None):
858 3524241a Christos Stavrakakis
    """Connect a network to nodegroups."""
859 bf5c82dc Christos Stavrakakis
    log.debug("Connecting network %s to backend %s", network, backend)
860 bf5c82dc Christos Stavrakakis
861 2d762302 Dimitris Aragiorgis
    if network.public:
862 2d762302 Dimitris Aragiorgis
        conflicts_check = True
863 2d762302 Dimitris Aragiorgis
    else:
864 2d762302 Dimitris Aragiorgis
        conflicts_check = False
865 2d762302 Dimitris Aragiorgis
866 d2036274 Christos Stavrakakis
    depends = create_job_dependencies(depends)
867 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
868 99af08a4 Christos Stavrakakis
        groups = [group] if group is not None else client.GetGroups()
869 99af08a4 Christos Stavrakakis
        job_ids = []
870 99af08a4 Christos Stavrakakis
        for group in groups:
871 99af08a4 Christos Stavrakakis
            job_id = client.ConnectNetwork(network.backend_id, group,
872 99af08a4 Christos Stavrakakis
                                           network.mode, network.link,
873 99af08a4 Christos Stavrakakis
                                           conflicts_check,
874 99af08a4 Christos Stavrakakis
                                           depends=depends)
875 99af08a4 Christos Stavrakakis
            job_ids.append(job_id)
876 99af08a4 Christos Stavrakakis
    return job_ids
877 22ee6892 Christos Stavrakakis
878 22ee6892 Christos Stavrakakis
879 99af08a4 Christos Stavrakakis
def delete_network(network, backend, disconnect=True):
880 99af08a4 Christos Stavrakakis
    log.debug("Deleting network %s from backend %s", network, backend)
881 22ee6892 Christos Stavrakakis
882 99af08a4 Christos Stavrakakis
    depends = []
883 99af08a4 Christos Stavrakakis
    if disconnect:
884 99af08a4 Christos Stavrakakis
        depends = disconnect_network(network, backend)
885 99af08a4 Christos Stavrakakis
    _delete_network(network, backend, depends=depends)
886 bf5c82dc Christos Stavrakakis
887 22ee6892 Christos Stavrakakis
888 99af08a4 Christos Stavrakakis
def _delete_network(network, backend, depends=[]):
889 d2036274 Christos Stavrakakis
    depends = create_job_dependencies(depends)
890 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
891 99af08a4 Christos Stavrakakis
        return client.DeleteNetwork(network.backend_id, depends)
892 22ee6892 Christos Stavrakakis
893 22ee6892 Christos Stavrakakis
894 3524241a Christos Stavrakakis
def disconnect_network(network, backend, group=None):
895 bf5c82dc Christos Stavrakakis
    log.debug("Disconnecting network %s to backend %s", network, backend)
896 22ee6892 Christos Stavrakakis
897 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
898 99af08a4 Christos Stavrakakis
        groups = [group] if group is not None else client.GetGroups()
899 99af08a4 Christos Stavrakakis
        job_ids = []
900 99af08a4 Christos Stavrakakis
        for group in groups:
901 99af08a4 Christos Stavrakakis
            job_id = client.DisconnectNetwork(network.backend_id, group)
902 99af08a4 Christos Stavrakakis
            job_ids.append(job_id)
903 99af08a4 Christos Stavrakakis
    return job_ids
904 36f4cb29 Christos Stavrakakis
905 36f4cb29 Christos Stavrakakis
906 2a2b01e5 Christos Stavrakakis
def connect_to_network(vm, nic):
907 2a2b01e5 Christos Stavrakakis
    network = nic.network
908 99af08a4 Christos Stavrakakis
    backend = vm.backend
909 d2036274 Christos Stavrakakis
    bnet, depend_jobs = ensure_network_is_active(backend, network.id)
910 99af08a4 Christos Stavrakakis
911 d2036274 Christos Stavrakakis
    depends = create_job_dependencies(depend_jobs)
912 99af08a4 Christos Stavrakakis
913 7c714455 Christos Stavrakakis
    nic = {'name': nic.backend_uuid,
914 7c714455 Christos Stavrakakis
           'network': network.backend_id,
915 8764d304 Christos Stavrakakis
           'ip': nic.ipv4_address}
916 22ee6892 Christos Stavrakakis
917 7c714455 Christos Stavrakakis
    log.debug("Adding NIC %s to VM %s", nic, vm)
918 22ee6892 Christos Stavrakakis
919 6488097c Christos Stavrakakis
    kwargs = {
920 6488097c Christos Stavrakakis
        "instance": vm.backend_vm_id,
921 1d74f135 Dimitris Aragiorgis
        "nics": [("add", "-1", nic)],
922 6488097c Christos Stavrakakis
        "depends": depends,
923 6488097c Christos Stavrakakis
    }
924 6488097c Christos Stavrakakis
    if vm.backend.use_hotplug():
925 6488097c Christos Stavrakakis
        kwargs["hotplug"] = True
926 6488097c Christos Stavrakakis
    if settings.TEST:
927 6488097c Christos Stavrakakis
        kwargs["dry_run"] = True
928 6488097c Christos Stavrakakis
929 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
930 6488097c Christos Stavrakakis
        return client.ModifyInstance(**kwargs)
931 22ee6892 Christos Stavrakakis
932 22ee6892 Christos Stavrakakis
933 3524241a Christos Stavrakakis
def disconnect_from_network(vm, nic):
934 7c714455 Christos Stavrakakis
    log.debug("Removing NIC %s of VM %s", nic, vm)
935 22ee6892 Christos Stavrakakis
936 6488097c Christos Stavrakakis
    kwargs = {
937 6488097c Christos Stavrakakis
        "instance": vm.backend_vm_id,
938 cd7ed999 Christos Stavrakakis
        "nics": [("remove", nic.backend_uuid, {})],
939 6488097c Christos Stavrakakis
    }
940 6488097c Christos Stavrakakis
    if vm.backend.use_hotplug():
941 6488097c Christos Stavrakakis
        kwargs["hotplug"] = True
942 6488097c Christos Stavrakakis
    if settings.TEST:
943 6488097c Christos Stavrakakis
        kwargs["dry_run"] = True
944 6488097c Christos Stavrakakis
945 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
946 6488097c Christos Stavrakakis
        jobID = client.ModifyInstance(**kwargs)
947 27435d42 Christos Stavrakakis
        firewall_profile = nic.firewall_profile
948 231b0fb6 Christos Stavrakakis
        if firewall_profile and firewall_profile != "DISABLED":
949 d0545590 Christos Stavrakakis
            tag = _firewall_tags[firewall_profile] % nic.backend_uuid
950 27435d42 Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, [tag],
951 27435d42 Christos Stavrakakis
                                      dry_run=settings.TEST)
952 27435d42 Christos Stavrakakis
953 27435d42 Christos Stavrakakis
        return jobID
954 91826390 Giorgos Verigakis
955 c25cc9ec Vangelis Koukis
956 d0545590 Christos Stavrakakis
def set_firewall_profile(vm, profile, nic):
957 d0545590 Christos Stavrakakis
    uuid = nic.backend_uuid
958 26563957 Giorgos Verigakis
    try:
959 d0545590 Christos Stavrakakis
        tag = _firewall_tags[profile] % uuid
960 26563957 Giorgos Verigakis
    except KeyError:
961 91826390 Giorgos Verigakis
        raise ValueError("Unsopported Firewall Profile: %s" % profile)
962 37ca953f Christodoulos Psaltis
963 d0545590 Christos Stavrakakis
    log.debug("Setting tag of VM %s, NIC %s, to %s", vm, nic, profile)
964 bf5c82dc Christos Stavrakakis
965 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
966 b2791a77 Christos Stavrakakis
        # Delete previous firewall tags
967 b2791a77 Christos Stavrakakis
        old_tags = client.GetInstanceTags(vm.backend_vm_id)
968 d0545590 Christos Stavrakakis
        delete_tags = [(t % uuid) for t in _firewall_tags.values()
969 d0545590 Christos Stavrakakis
                       if (t % uuid) in old_tags]
970 b2791a77 Christos Stavrakakis
        if delete_tags:
971 b2791a77 Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, delete_tags,
972 3524241a Christos Stavrakakis
                                      dry_run=settings.TEST)
973 37ca953f Christodoulos Psaltis
974 27435d42 Christos Stavrakakis
        if profile != "DISABLED":
975 27435d42 Christos Stavrakakis
            client.AddInstanceTags(vm.backend_vm_id, [tag],
976 27435d42 Christos Stavrakakis
                                   dry_run=settings.TEST)
977 9afeb669 Kostas Papadimitriou
978 3524241a Christos Stavrakakis
        # XXX NOP ModifyInstance call to force process_net_status to run
979 3524241a Christos Stavrakakis
        # on the dispatcher
980 cc92b70f Christos Stavrakakis
        os_name = settings.GANETI_CREATEINSTANCE_KWARGS['os']
981 3524241a Christos Stavrakakis
        client.ModifyInstance(vm.backend_vm_id,
982 cc92b70f Christos Stavrakakis
                              os_name=os_name)
983 41a7fae7 Christos Stavrakakis
    return None
984 5eedb0e4 Vangelis Koukis
985 41303ed0 Vangelis Koukis
986 e77a29ab Christos Stavrakakis
def get_instances(backend, bulk=True):
987 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
988 e77a29ab Christos Stavrakakis
        return c.GetInstances(bulk=bulk)
989 d986cb32 Christos Stavrakakis
990 f5b4f2a3 Christos Stavrakakis
991 e77a29ab Christos Stavrakakis
def get_nodes(backend, bulk=True):
992 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
993 e77a29ab Christos Stavrakakis
        return c.GetNodes(bulk=bulk)
994 1a894bfe Christos Stavrakakis
995 1a894bfe Christos Stavrakakis
996 70a0afab Christos Stavrakakis
def get_jobs(backend, bulk=True):
997 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
998 70a0afab Christos Stavrakakis
        return c.GetJobs(bulk=bulk)
999 f5b4f2a3 Christos Stavrakakis
1000 17852fe9 Giorgos Verigakis
1001 1a894bfe Christos Stavrakakis
def get_physical_resources(backend):
1002 1a894bfe Christos Stavrakakis
    """ Get the physical resources of a backend.
1003 1a894bfe Christos Stavrakakis

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

1006 1a894bfe Christos Stavrakakis
    """
1007 e77a29ab Christos Stavrakakis
    nodes = get_nodes(backend, bulk=True)
1008 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
1009 1a894bfe Christos Stavrakakis
    res = {}
1010 1a894bfe Christos Stavrakakis
    for a in attr:
1011 1a894bfe Christos Stavrakakis
        res[a] = 0
1012 1a894bfe Christos Stavrakakis
    for n in nodes:
1013 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
1014 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
1015 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
1016 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
1017 1a894bfe Christos Stavrakakis
            for a in attr:
1018 121b8921 Christos Stavrakakis
                res[a] += int(n[a] or 0)
1019 1a894bfe Christos Stavrakakis
    return res
1020 1a894bfe Christos Stavrakakis
1021 1a894bfe Christos Stavrakakis
1022 1da50fe3 Christos Stavrakakis
def update_backend_resources(backend, resources=None):
1023 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
1024 1a894bfe Christos Stavrakakis

1025 1a894bfe Christos Stavrakakis
    """
1026 17852fe9 Giorgos Verigakis
1027 1a894bfe Christos Stavrakakis
    if not resources:
1028 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
1029 41303ed0 Vangelis Koukis
1030 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
1031 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
1032 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
1033 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
1034 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
1035 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
1036 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
1037 1a894bfe Christos Stavrakakis
    backend.save()
1038 1a894bfe Christos Stavrakakis
1039 1a894bfe Christos Stavrakakis
1040 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
1041 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
1042 1a894bfe Christos Stavrakakis

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

1046 1a894bfe Christos Stavrakakis
    """
1047 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1048 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
1049 1a894bfe Christos Stavrakakis
    mem = 0
1050 1a894bfe Christos Stavrakakis
    for i in instances:
1051 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
1052 1a894bfe Christos Stavrakakis
    return mem
1053 b3d28af2 Christos Stavrakakis
1054 1da50fe3 Christos Stavrakakis
1055 1da50fe3 Christos Stavrakakis
def get_available_disk_templates(backend):
1056 1da50fe3 Christos Stavrakakis
    """Get the list of available disk templates of a Ganeti backend.
1057 1da50fe3 Christos Stavrakakis

1058 1da50fe3 Christos Stavrakakis
    The list contains the disk templates that are enabled in the Ganeti backend
1059 1da50fe3 Christos Stavrakakis
    and also included in ipolicy-disk-templates.
1060 1da50fe3 Christos Stavrakakis

1061 1da50fe3 Christos Stavrakakis
    """
1062 1da50fe3 Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
1063 1da50fe3 Christos Stavrakakis
        info = c.GetInfo()
1064 1da50fe3 Christos Stavrakakis
    ipolicy_disk_templates = info["ipolicy"]["disk-templates"]
1065 a8ae6989 Christos Stavrakakis
    try:
1066 a8ae6989 Christos Stavrakakis
        enabled_disk_templates = info["enabled_disk_templates"]
1067 a8ae6989 Christos Stavrakakis
        return [dp for dp in enabled_disk_templates
1068 a8ae6989 Christos Stavrakakis
                if dp in ipolicy_disk_templates]
1069 a8ae6989 Christos Stavrakakis
    except KeyError:
1070 a8ae6989 Christos Stavrakakis
        # Ganeti < 2.8 does not have 'enabled_disk_templates'
1071 a8ae6989 Christos Stavrakakis
        return ipolicy_disk_templates
1072 1da50fe3 Christos Stavrakakis
1073 1da50fe3 Christos Stavrakakis
1074 1da50fe3 Christos Stavrakakis
def update_backend_disk_templates(backend):
1075 1da50fe3 Christos Stavrakakis
    disk_templates = get_available_disk_templates(backend)
1076 1da50fe3 Christos Stavrakakis
    backend.disk_templates = disk_templates
1077 1da50fe3 Christos Stavrakakis
    backend.save()
1078 1da50fe3 Christos Stavrakakis
1079 1da50fe3 Christos Stavrakakis
1080 b3d28af2 Christos Stavrakakis
##
1081 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
1082 b3d28af2 Christos Stavrakakis
##
1083 b3d28af2 Christos Stavrakakis
1084 b3d28af2 Christos Stavrakakis
1085 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
1086 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
1087 d2036274 Christos Stavrakakis
    if result[0] != rapi.JOB_STATUS_SUCCESS:
1088 b3d28af2 Christos Stavrakakis
        return result
1089 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
1090 b3d28af2 Christos Stavrakakis
    return result
1091 b3d28af2 Christos Stavrakakis
1092 b3d28af2 Christos Stavrakakis
1093 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
1094 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1095 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
1096 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
1097 3524241a Christos Stavrakakis
    return result
1098 b3d28af2 Christos Stavrakakis
1099 b3d28af2 Christos Stavrakakis
1100 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
1101 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
1102 3524241a Christos Stavrakakis
        for group in client.GetGroups():
1103 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
1104 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
1105 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
1106 d2036274 Christos Stavrakakis
            if result[0] != rapi.JOB_STATUS_SUCCESS:
1107 3524241a Christos Stavrakakis
                return result
1108 b3d28af2 Christos Stavrakakis
1109 b3d28af2 Christos Stavrakakis
    return result
1110 b3d28af2 Christos Stavrakakis
1111 b3d28af2 Christos Stavrakakis
1112 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
1113 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
1114 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
1115 d2036274 Christos Stavrakakis
    while status not in rapi.JOB_STATUS_FINALIZED:
1116 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
1117 cc92b70f Christos Stavrakakis
                                         [result], None)
1118 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
1119 b3d28af2 Christos Stavrakakis
1120 d2036274 Christos Stavrakakis
    if status == rapi.JOB_STATUS_SUCCESS:
1121 b3d28af2 Christos Stavrakakis
        return (status, None)
1122 b3d28af2 Christos Stavrakakis
    else:
1123 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
1124 b3d28af2 Christos Stavrakakis
        return (status, error)
1125 d2036274 Christos Stavrakakis
1126 d2036274 Christos Stavrakakis
1127 d2036274 Christos Stavrakakis
def create_job_dependencies(job_ids=[], job_states=None):
1128 d2036274 Christos Stavrakakis
    """Transform a list of job IDs to Ganeti 'depends' attribute."""
1129 d2036274 Christos Stavrakakis
    if job_states is None:
1130 d2036274 Christos Stavrakakis
        job_states = list(rapi.JOB_STATUS_FINALIZED)
1131 d2036274 Christos Stavrakakis
    assert(type(job_states) == list)
1132 d2036274 Christos Stavrakakis
    return [[job_id, job_states] for job_id in job_ids]