Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (31.9 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 1a894bfe Christos Stavrakakis
from datetime import datetime
36 207b70d5 Giorgos Verigakis
37 22ee6892 Christos Stavrakakis
from synnefo.db.models import (Backend, VirtualMachine, Network,
38 72dea98f Christos Stavrakakis
                               FloatingIP,
39 3524241a Christos Stavrakakis
                               BackendNetwork, BACKEND_STATUSES,
40 ca4d59e3 Christos Stavrakakis
                               pooled_rapi_client, VirtualMachineDiagnostic,
41 ca4d59e3 Christos Stavrakakis
                               Flavor)
42 03992c72 Christos Stavrakakis
from synnefo.logic import utils
43 cb4eee84 Christos Stavrakakis
from synnefo import quotas
44 b7d38981 Dimitris Aragiorgis
from synnefo.api.util import release_resource
45 fd95834e Christos Stavrakakis
from synnefo.util.mac2eui64 import mac2eui64
46 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 02feca11 Vassilios Karakoidas
59 41a7fae7 Christos Stavrakakis
def handle_vm_quotas(vm, job_id, job_opcode, job_status, job_fields):
60 41a7fae7 Christos Stavrakakis
    """Handle quotas for updated VirtualMachine.
61 41a7fae7 Christos Stavrakakis

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

69 41a7fae7 Christos Stavrakakis
    """
70 41a7fae7 Christos Stavrakakis
    if job_status not in ["success", "error", "canceled"]:
71 41a7fae7 Christos Stavrakakis
        return
72 41a7fae7 Christos Stavrakakis
73 41a7fae7 Christos Stavrakakis
    # Check successful completion of a job will trigger any quotable change in
74 41a7fae7 Christos Stavrakakis
    # the VM state.
75 41a7fae7 Christos Stavrakakis
    action = utils.get_action_from_opcode(job_opcode, job_fields)
76 41a7fae7 Christos Stavrakakis
    commission_info = quotas.get_commission_info(vm, action=action,
77 41a7fae7 Christos Stavrakakis
                                                 action_fields=job_fields)
78 41a7fae7 Christos Stavrakakis
79 41a7fae7 Christos Stavrakakis
    if vm.task_job_id == job_id and vm.serial is not None:
80 41a7fae7 Christos Stavrakakis
        # Commission for this change has already been issued. So just
81 41a7fae7 Christos Stavrakakis
        # accept/reject it
82 41a7fae7 Christos Stavrakakis
        serial = vm.serial
83 41a7fae7 Christos Stavrakakis
        if job_status == "success":
84 41a7fae7 Christos Stavrakakis
            quotas.accept_serial(serial)
85 41a7fae7 Christos Stavrakakis
        elif job_status in ["error", "canceled"]:
86 41a7fae7 Christos Stavrakakis
            log.debug("Job %s failed. Rejecting related serial %s", job_id,
87 41a7fae7 Christos Stavrakakis
                      serial)
88 41a7fae7 Christos Stavrakakis
            quotas.reject_serial(serial)
89 41a7fae7 Christos Stavrakakis
        vm.serial = None
90 41a7fae7 Christos Stavrakakis
    elif job_status == "success" and commission_info is not None:
91 41a7fae7 Christos Stavrakakis
        log.debug("Expected job was %s. Processing job %s. Commission for"
92 41a7fae7 Christos Stavrakakis
                  " this job: %s", vm.task_job_id, job_id, commission_info)
93 41a7fae7 Christos Stavrakakis
        # Commission for this change has not been issued, or the issued
94 41a7fae7 Christos Stavrakakis
        # commission was unaware of the current change. Reject all previous
95 41a7fae7 Christos Stavrakakis
        # commissions and create a new one in forced mode!
96 41a7fae7 Christos Stavrakakis
        previous_serial = vm.serial
97 41a7fae7 Christos Stavrakakis
        if previous_serial and not previous_serial.resolved:
98 41a7fae7 Christos Stavrakakis
            quotas.resolve_vm_commission(previous_serial)
99 41a7fae7 Christos Stavrakakis
        serial = quotas.issue_commission(user=vm.userid,
100 41a7fae7 Christos Stavrakakis
                                         source=quotas.DEFAULT_SOURCE,
101 41a7fae7 Christos Stavrakakis
                                         provisions=commission_info,
102 41a7fae7 Christos Stavrakakis
                                         force=True,
103 41a7fae7 Christos Stavrakakis
                                         auto_accept=True)
104 41a7fae7 Christos Stavrakakis
        # Clear VM's serial. Expected job may arrive later. However correlated
105 41a7fae7 Christos Stavrakakis
        # serial must not be accepted, since it reflects a previous VM state
106 41a7fae7 Christos Stavrakakis
        vm.serial = None
107 41a7fae7 Christos Stavrakakis
108 41a7fae7 Christos Stavrakakis
    return vm
109 41a7fae7 Christos Stavrakakis
110 41a7fae7 Christos Stavrakakis
111 093f9c53 Vangelis Koukis
@transaction.commit_on_success
112 ca4d59e3 Christos Stavrakakis
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None,
113 ca4d59e3 Christos Stavrakakis
                      beparams=None):
114 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
115 02feca11 Vassilios Karakoidas

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

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

212 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
213 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
214 ad2d6807 Vangelis Koukis

215 ad2d6807 Vangelis Koukis
    Update the state of the VM in the DB accordingly.
216 ad2d6807 Vangelis Koukis
    """
217 37ca953f Christodoulos Psaltis
218 b578d9e7 Christos Stavrakakis
    ganeti_nics = process_ganeti_nics(nics)
219 b578d9e7 Christos Stavrakakis
    if not nics_changed(vm.nics.order_by('index'), ganeti_nics):
220 b578d9e7 Christos Stavrakakis
        log.debug("NICs for VM %s have not changed", vm)
221 40d53b77 Christos Stavrakakis
        return
222 40d53b77 Christos Stavrakakis
223 40d53b77 Christos Stavrakakis
    # Get X-Lock on backend before getting X-Lock on network IP pools, to
224 40d53b77 Christos Stavrakakis
    # guarantee that no deadlock will occur with Backend allocator.
225 40d53b77 Christos Stavrakakis
    Backend.objects.select_for_update().get(id=vm.backend_id)
226 b578d9e7 Christos Stavrakakis
227 72dea98f Christos Stavrakakis
    # NICs have changed. Release the instance IPs
228 72dea98f Christos Stavrakakis
    release_instance_ips(vm, ganeti_nics)
229 72dea98f Christos Stavrakakis
    # And delete the releated NICs (must be performed after release!)
230 72dea98f Christos Stavrakakis
    vm.nics.all().delete()
231 77f0fa63 Christos Stavrakakis
232 b578d9e7 Christos Stavrakakis
    for nic in ganeti_nics:
233 b578d9e7 Christos Stavrakakis
        ipv4 = nic.get('ipv4', '')
234 f45a7ac4 Christos Stavrakakis
        net = nic['network']
235 b578d9e7 Christos Stavrakakis
        if ipv4:
236 b578d9e7 Christos Stavrakakis
            net.reserve_address(ipv4)
237 b578d9e7 Christos Stavrakakis
238 b578d9e7 Christos Stavrakakis
        nic['dirty'] = False
239 b578d9e7 Christos Stavrakakis
        vm.nics.create(**nic)
240 b578d9e7 Christos Stavrakakis
        # Dummy save the network, because UI uses changed-since for VMs
241 b578d9e7 Christos Stavrakakis
        # and Networks in order to show the VM NICs
242 b578d9e7 Christos Stavrakakis
        net.save()
243 b578d9e7 Christos Stavrakakis
244 b578d9e7 Christos Stavrakakis
    vm.backendtime = etime
245 b578d9e7 Christos Stavrakakis
    vm.save()
246 b578d9e7 Christos Stavrakakis
247 b578d9e7 Christos Stavrakakis
248 b578d9e7 Christos Stavrakakis
def process_ganeti_nics(ganeti_nics):
249 b578d9e7 Christos Stavrakakis
    """Process NIC dict from ganeti hooks."""
250 b578d9e7 Christos Stavrakakis
    new_nics = []
251 b578d9e7 Christos Stavrakakis
    for i, new_nic in enumerate(ganeti_nics):
252 77f0fa63 Christos Stavrakakis
        network = new_nic.get('network', '')
253 22ee6892 Christos Stavrakakis
        n = str(network)
254 77f0fa63 Christos Stavrakakis
        pk = utils.id_from_network_name(n)
255 77f0fa63 Christos Stavrakakis
256 fdc94944 Christos Stavrakakis
        net = Network.objects.get(pk=pk)
257 77f0fa63 Christos Stavrakakis
258 77f0fa63 Christos Stavrakakis
        # Get the new nic info
259 77f0fa63 Christos Stavrakakis
        mac = new_nic.get('mac', '')
260 77f0fa63 Christos Stavrakakis
        ipv4 = new_nic.get('ip', '')
261 fd95834e Christos Stavrakakis
        if net.subnet6:
262 fd95834e Christos Stavrakakis
            ipv6 = mac2eui64(mac, net.subnet6)
263 fd95834e Christos Stavrakakis
        else:
264 fd95834e Christos Stavrakakis
            ipv6 = ''
265 9afeb669 Kostas Papadimitriou
266 77f0fa63 Christos Stavrakakis
        firewall = new_nic.get('firewall', '')
267 658a825a Giorgos Verigakis
        firewall_profile = _reverse_tags.get(firewall, '')
268 658a825a Giorgos Verigakis
        if not firewall_profile and net.public:
269 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
270 9afeb669 Kostas Papadimitriou
271 b578d9e7 Christos Stavrakakis
        nic = {
272 cc92b70f Christos Stavrakakis
            'index': i,
273 cc92b70f Christos Stavrakakis
            'network': net,
274 cc92b70f Christos Stavrakakis
            'mac': mac,
275 cc92b70f Christos Stavrakakis
            'ipv4': ipv4,
276 cc92b70f Christos Stavrakakis
            'ipv6': ipv6,
277 939d71dd Christos Stavrakakis
            'firewall_profile': firewall_profile,
278 939d71dd Christos Stavrakakis
            'state': 'ACTIVE'}
279 b578d9e7 Christos Stavrakakis
280 b578d9e7 Christos Stavrakakis
        new_nics.append(nic)
281 b578d9e7 Christos Stavrakakis
    return new_nics
282 b578d9e7 Christos Stavrakakis
283 b578d9e7 Christos Stavrakakis
284 b578d9e7 Christos Stavrakakis
def nics_changed(old_nics, new_nics):
285 b578d9e7 Christos Stavrakakis
    """Return True if NICs have changed in any way."""
286 b578d9e7 Christos Stavrakakis
    if len(old_nics) != len(new_nics):
287 b578d9e7 Christos Stavrakakis
        return True
288 be4d8aed Christos Stavrakakis
    fields = ["ipv4", "ipv6", "mac", "firewall_profile", "index", "network"]
289 b578d9e7 Christos Stavrakakis
    for old_nic, new_nic in zip(old_nics, new_nics):
290 be4d8aed Christos Stavrakakis
        for field in fields:
291 be4d8aed Christos Stavrakakis
            if getattr(old_nic, field) != new_nic[field]:
292 be4d8aed Christos Stavrakakis
                return True
293 b578d9e7 Christos Stavrakakis
    return False
294 22ee6892 Christos Stavrakakis
295 22ee6892 Christos Stavrakakis
296 72dea98f Christos Stavrakakis
def release_instance_ips(vm, ganeti_nics):
297 72dea98f Christos Stavrakakis
    old_addresses = set(vm.nics.values_list("network", "ipv4"))
298 f8675683 Christos Stavrakakis
    new_addresses = set(map(lambda nic: (nic["network"].id, nic["ipv4"]),
299 72dea98f Christos Stavrakakis
                            ganeti_nics))
300 72dea98f Christos Stavrakakis
    to_release = old_addresses - new_addresses
301 72dea98f Christos Stavrakakis
    for (network_id, ipv4) in to_release:
302 72dea98f Christos Stavrakakis
        if ipv4:
303 72dea98f Christos Stavrakakis
            net = Network.objects.get(id=network_id)
304 72dea98f Christos Stavrakakis
            # Important: Take exclusive lock in pool before checking if there
305 72dea98f Christos Stavrakakis
            # is a floating IP with this ipv4 address, otherwise there is a
306 72dea98f Christos Stavrakakis
            # race condition, where you may release a floating IP that has been
307 72dea98f Christos Stavrakakis
            # created after search floating IPs and before you get exclusively
308 72dea98f Christos Stavrakakis
            # the pool
309 72dea98f Christos Stavrakakis
            pool = net.get_pool()
310 72dea98f Christos Stavrakakis
            try:
311 72dea98f Christos Stavrakakis
                floating_ip = net.floating_ips.select_for_update()\
312 72dea98f Christos Stavrakakis
                                              .get(ipv4=ipv4, machine=vm,
313 72dea98f Christos Stavrakakis
                                                   deleted=False)
314 72dea98f Christos Stavrakakis
                floating_ip.machine = None
315 72dea98f Christos Stavrakakis
                floating_ip.save()
316 72dea98f Christos Stavrakakis
            except FloatingIP.DoesNotExist:
317 72dea98f Christos Stavrakakis
                net.release_address(ipv4)
318 72dea98f Christos Stavrakakis
                pool.save()
319 bd392934 Christos Stavrakakis
320 bd392934 Christos Stavrakakis
321 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
322 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
323 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
324 fd2bdbb2 Christos Stavrakakis
        raise Network.InvalidBackendMsgError(opcode, status)
325 22ee6892 Christos Stavrakakis
326 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
327 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
328 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
329 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
330 22ee6892 Christos Stavrakakis
331 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
332 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
333 22ee6892 Christos Stavrakakis
    if status == 'success' and state_for_success is not None:
334 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
335 22ee6892 Christos Stavrakakis
336 e6f6627c Christos Stavrakakis
    if status in ('canceled', 'error') and opcode == 'OP_NETWORK_ADD':
337 5480daec Christos Stavrakakis
        back_network.operstate = 'ERROR'
338 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
339 22ee6892 Christos Stavrakakis
340 e97288bc Christos Stavrakakis
    if opcode == 'OP_NETWORK_REMOVE':
341 e97288bc Christos Stavrakakis
        if status == 'success' or (status == 'error' and
342 e97288bc Christos Stavrakakis
                                   back_network.operstate == 'ERROR'):
343 e97288bc Christos Stavrakakis
            back_network.operstate = state_for_success
344 e97288bc Christos Stavrakakis
            back_network.deleted = True
345 e97288bc Christos Stavrakakis
            back_network.backendtime = etime
346 22ee6892 Christos Stavrakakis
347 e97288bc Christos Stavrakakis
    if status == 'success':
348 e97288bc Christos Stavrakakis
        back_network.backendtime = etime
349 22ee6892 Christos Stavrakakis
    back_network.save()
350 4d5d0b9c Christos Stavrakakis
    # Also you must update the state of the Network!!
351 4d5d0b9c Christos Stavrakakis
    update_network_state(back_network.network)
352 fd2bdbb2 Christos Stavrakakis
353 fd2bdbb2 Christos Stavrakakis
354 2509ce17 Christos Stavrakakis
def update_network_state(network):
355 99af08a4 Christos Stavrakakis
    """Update the state of a Network based on BackendNetwork states.
356 cb4eee84 Christos Stavrakakis

357 99af08a4 Christos Stavrakakis
    Update the state of a Network based on the operstate of the networks in the
358 99af08a4 Christos Stavrakakis
    backends that network exists.
359 99af08a4 Christos Stavrakakis

360 99af08a4 Christos Stavrakakis
    The state of the network is:
361 99af08a4 Christos Stavrakakis
    * ACTIVE: If it is 'ACTIVE' in at least one backend.
362 99af08a4 Christos Stavrakakis
    * DELETED: If it is is 'DELETED' in all backends that have been created.
363 99af08a4 Christos Stavrakakis

364 99af08a4 Christos Stavrakakis
    This function also releases the resources (MAC prefix or Bridge) and the
365 99af08a4 Christos Stavrakakis
    quotas for the network.
366 99af08a4 Christos Stavrakakis

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

472 7d43565f Kostas Papadimitriou
    :param vm: VirtualMachine instance to create diagnostic for.
473 7d43565f Kostas Papadimitriou
    :param message: Diagnostic message.
474 7d43565f Kostas Papadimitriou
    :param source: Diagnostic source identifier (e.g. image-helper).
475 7d43565f Kostas Papadimitriou
    :param level: Diagnostic level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
476 7d43565f Kostas Papadimitriou
    :param etime: The time the message occured (if available).
477 7d43565f Kostas Papadimitriou
    :param details: Additional details or debug information.
478 7d43565f Kostas Papadimitriou
    """
479 7d43565f Kostas Papadimitriou
    VirtualMachineDiagnostic.objects.create_for_vm(vm, level, source=source,
480 cc92b70f Christos Stavrakakis
                                                   source_date=etime,
481 cc92b70f Christos Stavrakakis
                                                   message=message,
482 cc92b70f Christos Stavrakakis
                                                   details=details)
483 7d43565f Kostas Papadimitriou
484 7d43565f Kostas Papadimitriou
485 2c022086 Christos Stavrakakis
def create_instance(vm, nics, flavor, image):
486 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
487 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
488 7f691719 Christos Stavrakakis

489 3a9b3cde Giorgos Verigakis
        metadata value should be a dictionary.
490 3a9b3cde Giorgos Verigakis
    """
491 864bed43 Christos Stavrakakis
492 1c382247 Vangelis Koukis
    # Handle arguments to CreateInstance() as a dictionary,
493 1c382247 Vangelis Koukis
    # initialize it based on a deployment-specific value.
494 1c382247 Vangelis Koukis
    # This enables the administrator to override deployment-specific
495 c25cc9ec Vangelis Koukis
    # arguments, such as the disk template to use, name of os provider
496 1c382247 Vangelis Koukis
    # and hypervisor-specific parameters at will (see Synnefo #785, #835).
497 1c382247 Vangelis Koukis
    #
498 bd87213f Christos Stavrakakis
    kw = vm.backend.get_create_params()
499 1c382247 Vangelis Koukis
    kw['mode'] = 'create'
500 924d8085 Christos Stavrakakis
    kw['name'] = vm.backend_vm_id
501 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
502 296682fe Kostas Papadimitriou
503 2a599282 Christos Stavrakakis
    kw['disk_template'] = flavor.disk_template
504 d7841399 Christos Stavrakakis
    kw['disks'] = [{"size": flavor.disk * 1024}]
505 006c6249 Christos Stavrakakis
    provider = flavor.disk_provider
506 296682fe Kostas Papadimitriou
    if provider:
507 296682fe Kostas Papadimitriou
        kw['disks'][0]['provider'] = provider
508 b2222a7f Christos Stavrakakis
        kw['disks'][0]['origin'] = flavor.disk_origin
509 296682fe Kostas Papadimitriou
510 cb66110b Christos Stavrakakis
    kw['nics'] = [{"network": nic.network.backend_id, "ip": nic.ipv4}
511 cb66110b Christos Stavrakakis
                  for nic in nics]
512 cb66110b Christos Stavrakakis
    backend = vm.backend
513 cb66110b Christos Stavrakakis
    depend_jobs = []
514 cb66110b Christos Stavrakakis
    for nic in nics:
515 cb66110b Christos Stavrakakis
        network = Network.objects.select_for_update().get(id=nic.network.id)
516 cb66110b Christos Stavrakakis
        bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
517 cb66110b Christos Stavrakakis
                                                             network=network)
518 cb66110b Christos Stavrakakis
        if bnet.operstate != "ACTIVE":
519 cb66110b Christos Stavrakakis
            if network.public:
520 cb66110b Christos Stavrakakis
                # TODO: What to raise here ?
521 cb66110b Christos Stavrakakis
                raise Exception("LALA")
522 cb66110b Christos Stavrakakis
            else:
523 cb66110b Christos Stavrakakis
                depend_jobs.append(create_network(network, backend,
524 cb66110b Christos Stavrakakis
                                                  connect=True))
525 cb66110b Christos Stavrakakis
    kw["depends"] = [[job, ["success", "error", "canceled"]]
526 cb66110b Christos Stavrakakis
                     for job in depend_jobs]
527 cb66110b Christos Stavrakakis
528 bd87213f Christos Stavrakakis
    if vm.backend.use_hotplug():
529 341c818e Dimitris Aragiorgis
        kw['hotplug'] = True
530 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
531 1c382247 Vangelis Koukis
    # kw['os'] = settings.GANETI_OS_PROVIDER
532 1c382247 Vangelis Koukis
    kw['ip_check'] = False
533 1c382247 Vangelis Koukis
    kw['name_check'] = False
534 79b7dbb7 Christos Stavrakakis
535 1c382247 Vangelis Koukis
    # Do not specific a node explicitly, have
536 1c382247 Vangelis Koukis
    # Ganeti use an iallocator instead
537 f8d1eaf4 Kostas Papadimitriou
    #kw['pnode'] = rapi.GetNodes()[0]
538 79b7dbb7 Christos Stavrakakis
539 1c382247 Vangelis Koukis
    kw['dry_run'] = settings.TEST
540 41303ed0 Vangelis Koukis
541 2b1db26f Giorgos Verigakis
    kw['beparams'] = {
542 cc92b70f Christos Stavrakakis
        'auto_balance': True,
543 cc92b70f Christos Stavrakakis
        'vcpus': flavor.cpu,
544 cc92b70f Christos Stavrakakis
        'memory': flavor.ram}
545 79b7dbb7 Christos Stavrakakis
546 e3b5be49 Giorgos Verigakis
    kw['osparams'] = {
547 79b7dbb7 Christos Stavrakakis
        'config_url': vm.config_url,
548 79b7dbb7 Christos Stavrakakis
        # Store image id and format to Ganeti
549 2a599282 Christos Stavrakakis
        'img_id': image['backend_id'],
550 3a9b3cde Giorgos Verigakis
        'img_format': image['format']}
551 d1eaa651 Christos Stavrakakis
552 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
553 1c382247 Vangelis Koukis
    # kw['hvparams'] = dict(serial_console=False)
554 79b7dbb7 Christos Stavrakakis
555 6afeb85d Christos Stavrakakis
    log.debug("Creating instance %s", utils.hide_pass(kw))
556 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
557 3524241a Christos Stavrakakis
        return client.CreateInstance(**kw)
558 f533f224 Vangelis Koukis
559 529178b1 Giorgos Verigakis
560 529178b1 Giorgos Verigakis
def delete_instance(vm):
561 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
562 3524241a Christos Stavrakakis
        return client.DeleteInstance(vm.backend_vm_id, dry_run=settings.TEST)
563 529178b1 Giorgos Verigakis
564 ad2d6807 Vangelis Koukis
565 529178b1 Giorgos Verigakis
def reboot_instance(vm, reboot_type):
566 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
567 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
568 bf5c82dc Christos Stavrakakis
        return client.RebootInstance(vm.backend_vm_id, reboot_type,
569 bf5c82dc Christos Stavrakakis
                                     dry_run=settings.TEST)
570 529178b1 Giorgos Verigakis
571 ad2d6807 Vangelis Koukis
572 529178b1 Giorgos Verigakis
def startup_instance(vm):
573 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
574 3524241a Christos Stavrakakis
        return client.StartupInstance(vm.backend_vm_id, dry_run=settings.TEST)
575 529178b1 Giorgos Verigakis
576 ad2d6807 Vangelis Koukis
577 529178b1 Giorgos Verigakis
def shutdown_instance(vm):
578 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
579 3524241a Christos Stavrakakis
        return client.ShutdownInstance(vm.backend_vm_id, dry_run=settings.TEST)
580 529178b1 Giorgos Verigakis
581 ad2d6807 Vangelis Koukis
582 2cd3f389 Christos Stavrakakis
def resize_instance(vm, vcpus, memory):
583 2cd3f389 Christos Stavrakakis
    beparams = {"vcpus": int(vcpus),
584 2cd3f389 Christos Stavrakakis
                "minmem": int(memory),
585 2cd3f389 Christos Stavrakakis
                "maxmem": int(memory)}
586 2cd3f389 Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
587 2cd3f389 Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, beparams=beparams)
588 2cd3f389 Christos Stavrakakis
589 2cd3f389 Christos Stavrakakis
590 529178b1 Giorgos Verigakis
def get_instance_console(vm):
591 71099804 Vangelis Koukis
    # RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
592 71099804 Vangelis Koukis
    # which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
593 71099804 Vangelis Koukis
    # useless (see #783).
594 71099804 Vangelis Koukis
    #
595 71099804 Vangelis Koukis
    # Until this is fixed on the Ganeti side, construct a console info reply
596 71099804 Vangelis Koukis
    # directly.
597 9afeb669 Kostas Papadimitriou
    #
598 71099804 Vangelis Koukis
    # WARNING: This assumes that VNC runs on port network_port on
599 71099804 Vangelis Koukis
    #          the instance's primary node, and is probably
600 71099804 Vangelis Koukis
    #          hypervisor-specific.
601 71099804 Vangelis Koukis
    #
602 bf5c82dc Christos Stavrakakis
    log.debug("Getting console for vm %s", vm)
603 bf5c82dc Christos Stavrakakis
604 71099804 Vangelis Koukis
    console = {}
605 71099804 Vangelis Koukis
    console['kind'] = 'vnc'
606 3524241a Christos Stavrakakis
607 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
608 3524241a Christos Stavrakakis
        i = client.GetInstance(vm.backend_vm_id)
609 3524241a Christos Stavrakakis
610 bd87213f Christos Stavrakakis
    if vm.backend.hypervisor == "kvm" and i['hvparams']['serial_console']:
611 71099804 Vangelis Koukis
        raise Exception("hv parameter serial_console cannot be true")
612 71099804 Vangelis Koukis
    console['host'] = i['pnode']
613 71099804 Vangelis Koukis
    console['port'] = i['network_port']
614 9afeb669 Kostas Papadimitriou
615 71099804 Vangelis Koukis
    return console
616 604b2bf8 Georgios Gousios
617 604b2bf8 Georgios Gousios
618 3524241a Christos Stavrakakis
def get_instance_info(vm):
619 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
620 3524241a Christos Stavrakakis
        return client.GetInstanceInfo(vm.backend_vm_id)
621 f533f224 Vangelis Koukis
622 c25cc9ec Vangelis Koukis
623 99af08a4 Christos Stavrakakis
def create_network(network, backend, connect=True):
624 99af08a4 Christos Stavrakakis
    """Create a network in a Ganeti backend"""
625 99af08a4 Christos Stavrakakis
    log.debug("Creating network %s in backend %s", network, backend)
626 64938cb0 Giorgos Verigakis
627 99af08a4 Christos Stavrakakis
    job_id = _create_network(network, backend)
628 c25cc9ec Vangelis Koukis
629 99af08a4 Christos Stavrakakis
    if connect:
630 99af08a4 Christos Stavrakakis
        job_ids = connect_network(network, backend, depends=[job_id])
631 99af08a4 Christos Stavrakakis
        return job_ids
632 99af08a4 Christos Stavrakakis
    else:
633 99af08a4 Christos Stavrakakis
        return [job_id]
634 37ca953f Christodoulos Psaltis
635 64938cb0 Giorgos Verigakis
636 3524241a Christos Stavrakakis
def _create_network(network, backend):
637 3524241a Christos Stavrakakis
    """Create a network."""
638 c25cc9ec Vangelis Koukis
639 22ee6892 Christos Stavrakakis
    network_type = network.public and 'public' or 'private'
640 22ee6892 Christos Stavrakakis
641 22ee6892 Christos Stavrakakis
    tags = network.backend_tag
642 22ee6892 Christos Stavrakakis
    if network.dhcp:
643 22ee6892 Christos Stavrakakis
        tags.append('nfdhcpd')
644 2d762302 Dimitris Aragiorgis
645 2d762302 Dimitris Aragiorgis
    if network.public:
646 2d762302 Dimitris Aragiorgis
        conflicts_check = True
647 2d762302 Dimitris Aragiorgis
    else:
648 2d762302 Dimitris Aragiorgis
        conflicts_check = False
649 22ee6892 Christos Stavrakakis
650 3524241a Christos Stavrakakis
    try:
651 3524241a Christos Stavrakakis
        bn = BackendNetwork.objects.get(network=network, backend=backend)
652 3524241a Christos Stavrakakis
        mac_prefix = bn.mac_prefix
653 3524241a Christos Stavrakakis
    except BackendNetwork.DoesNotExist:
654 cc92b70f Christos Stavrakakis
        raise Exception("BackendNetwork for network '%s' in backend '%s'"
655 3524241a Christos Stavrakakis
                        " does not exist" % (network.id, backend.id))
656 3524241a Christos Stavrakakis
657 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
658 3524241a Christos Stavrakakis
        return client.CreateNetwork(network_name=network.backend_id,
659 3524241a Christos Stavrakakis
                                    network=network.subnet,
660 6cc3a31c Christos Stavrakakis
                                    network6=network.subnet6,
661 3524241a Christos Stavrakakis
                                    gateway=network.gateway,
662 6cc3a31c Christos Stavrakakis
                                    gateway6=network.gateway6,
663 3524241a Christos Stavrakakis
                                    network_type=network_type,
664 3524241a Christos Stavrakakis
                                    mac_prefix=mac_prefix,
665 2d762302 Dimitris Aragiorgis
                                    conflicts_check=conflicts_check,
666 3524241a Christos Stavrakakis
                                    tags=tags)
667 3524241a Christos Stavrakakis
668 3524241a Christos Stavrakakis
669 99af08a4 Christos Stavrakakis
def connect_network(network, backend, depends=[], group=None):
670 3524241a Christos Stavrakakis
    """Connect a network to nodegroups."""
671 bf5c82dc Christos Stavrakakis
    log.debug("Connecting network %s to backend %s", network, backend)
672 bf5c82dc Christos Stavrakakis
673 2d762302 Dimitris Aragiorgis
    if network.public:
674 2d762302 Dimitris Aragiorgis
        conflicts_check = True
675 2d762302 Dimitris Aragiorgis
    else:
676 2d762302 Dimitris Aragiorgis
        conflicts_check = False
677 2d762302 Dimitris Aragiorgis
678 99af08a4 Christos Stavrakakis
    depends = [[job, ["success", "error", "canceled"]] for job in depends]
679 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
680 99af08a4 Christos Stavrakakis
        groups = [group] if group is not None else client.GetGroups()
681 99af08a4 Christos Stavrakakis
        job_ids = []
682 99af08a4 Christos Stavrakakis
        for group in groups:
683 99af08a4 Christos Stavrakakis
            job_id = client.ConnectNetwork(network.backend_id, group,
684 99af08a4 Christos Stavrakakis
                                           network.mode, network.link,
685 99af08a4 Christos Stavrakakis
                                           conflicts_check,
686 99af08a4 Christos Stavrakakis
                                           depends=depends)
687 99af08a4 Christos Stavrakakis
            job_ids.append(job_id)
688 99af08a4 Christos Stavrakakis
    return job_ids
689 22ee6892 Christos Stavrakakis
690 22ee6892 Christos Stavrakakis
691 99af08a4 Christos Stavrakakis
def delete_network(network, backend, disconnect=True):
692 99af08a4 Christos Stavrakakis
    log.debug("Deleting network %s from backend %s", network, backend)
693 22ee6892 Christos Stavrakakis
694 99af08a4 Christos Stavrakakis
    depends = []
695 99af08a4 Christos Stavrakakis
    if disconnect:
696 99af08a4 Christos Stavrakakis
        depends = disconnect_network(network, backend)
697 99af08a4 Christos Stavrakakis
    _delete_network(network, backend, depends=depends)
698 bf5c82dc Christos Stavrakakis
699 22ee6892 Christos Stavrakakis
700 99af08a4 Christos Stavrakakis
def _delete_network(network, backend, depends=[]):
701 99af08a4 Christos Stavrakakis
    depends = [[job, ["success", "error", "canceled"]] for job in depends]
702 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
703 99af08a4 Christos Stavrakakis
        return client.DeleteNetwork(network.backend_id, depends)
704 22ee6892 Christos Stavrakakis
705 22ee6892 Christos Stavrakakis
706 3524241a Christos Stavrakakis
def disconnect_network(network, backend, group=None):
707 bf5c82dc Christos Stavrakakis
    log.debug("Disconnecting network %s to backend %s", network, backend)
708 22ee6892 Christos Stavrakakis
709 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
710 99af08a4 Christos Stavrakakis
        groups = [group] if group is not None else client.GetGroups()
711 99af08a4 Christos Stavrakakis
        job_ids = []
712 99af08a4 Christos Stavrakakis
        for group in groups:
713 99af08a4 Christos Stavrakakis
            job_id = client.DisconnectNetwork(network.backend_id, group)
714 99af08a4 Christos Stavrakakis
            job_ids.append(job_id)
715 99af08a4 Christos Stavrakakis
    return job_ids
716 36f4cb29 Christos Stavrakakis
717 36f4cb29 Christos Stavrakakis
718 87920bc3 Christos Stavrakakis
def connect_to_network(vm, network, address=None):
719 99af08a4 Christos Stavrakakis
    backend = vm.backend
720 acfc71ef Christos Stavrakakis
    network = Network.objects.select_for_update().get(id=network.id)
721 99af08a4 Christos Stavrakakis
    bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
722 99af08a4 Christos Stavrakakis
                                                         network=network)
723 99af08a4 Christos Stavrakakis
    depend_jobs = []
724 99af08a4 Christos Stavrakakis
    if bnet.operstate != "ACTIVE":
725 99af08a4 Christos Stavrakakis
        depend_jobs = create_network(network, backend, connect=True)
726 99af08a4 Christos Stavrakakis
727 99af08a4 Christos Stavrakakis
    depends = [[job, ["success", "error", "canceled"]] for job in depend_jobs]
728 99af08a4 Christos Stavrakakis
729 3524241a Christos Stavrakakis
    nic = {'ip': address, 'network': network.backend_id}
730 22ee6892 Christos Stavrakakis
731 bf5c82dc Christos Stavrakakis
    log.debug("Connecting vm %s to network %s(%s)", vm, network, address)
732 22ee6892 Christos Stavrakakis
733 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
734 3524241a Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, nics=[('add',  nic)],
735 bd87213f Christos Stavrakakis
                                     hotplug=vm.backend.use_hotplug(),
736 99af08a4 Christos Stavrakakis
                                     depends=depends,
737 3524241a Christos Stavrakakis
                                     dry_run=settings.TEST)
738 22ee6892 Christos Stavrakakis
739 22ee6892 Christos Stavrakakis
740 3524241a Christos Stavrakakis
def disconnect_from_network(vm, nic):
741 3524241a Christos Stavrakakis
    op = [('remove', nic.index, {})]
742 22ee6892 Christos Stavrakakis
743 bf5c82dc Christos Stavrakakis
    log.debug("Removing nic of VM %s, with index %s", vm, str(nic.index))
744 22ee6892 Christos Stavrakakis
745 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
746 3524241a Christos Stavrakakis
        return client.ModifyInstance(vm.backend_vm_id, nics=op,
747 bd87213f Christos Stavrakakis
                                     hotplug=vm.backend.use_hotplug(),
748 3524241a Christos Stavrakakis
                                     dry_run=settings.TEST)
749 91826390 Giorgos Verigakis
750 c25cc9ec Vangelis Koukis
751 91826390 Giorgos Verigakis
def set_firewall_profile(vm, profile):
752 26563957 Giorgos Verigakis
    try:
753 26563957 Giorgos Verigakis
        tag = _firewall_tags[profile]
754 26563957 Giorgos Verigakis
    except KeyError:
755 91826390 Giorgos Verigakis
        raise ValueError("Unsopported Firewall Profile: %s" % profile)
756 37ca953f Christodoulos Psaltis
757 bf5c82dc Christos Stavrakakis
    log.debug("Setting tag of VM %s to %s", vm, profile)
758 bf5c82dc Christos Stavrakakis
759 3524241a Christos Stavrakakis
    with pooled_rapi_client(vm) as client:
760 3524241a Christos Stavrakakis
        # Delete all firewall tags
761 3524241a Christos Stavrakakis
        for t in _firewall_tags.values():
762 3524241a Christos Stavrakakis
            client.DeleteInstanceTags(vm.backend_vm_id, [t],
763 3524241a Christos Stavrakakis
                                      dry_run=settings.TEST)
764 37ca953f Christodoulos Psaltis
765 3524241a Christos Stavrakakis
        client.AddInstanceTags(vm.backend_vm_id, [tag], dry_run=settings.TEST)
766 9afeb669 Kostas Papadimitriou
767 3524241a Christos Stavrakakis
        # XXX NOP ModifyInstance call to force process_net_status to run
768 3524241a Christos Stavrakakis
        # on the dispatcher
769 cc92b70f Christos Stavrakakis
        os_name = settings.GANETI_CREATEINSTANCE_KWARGS['os']
770 3524241a Christos Stavrakakis
        client.ModifyInstance(vm.backend_vm_id,
771 cc92b70f Christos Stavrakakis
                              os_name=os_name)
772 41a7fae7 Christos Stavrakakis
    return None
773 5eedb0e4 Vangelis Koukis
774 41303ed0 Vangelis Koukis
775 e77a29ab Christos Stavrakakis
def get_instances(backend, bulk=True):
776 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
777 e77a29ab Christos Stavrakakis
        return c.GetInstances(bulk=bulk)
778 d986cb32 Christos Stavrakakis
779 f5b4f2a3 Christos Stavrakakis
780 e77a29ab Christos Stavrakakis
def get_nodes(backend, bulk=True):
781 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
782 e77a29ab Christos Stavrakakis
        return c.GetNodes(bulk=bulk)
783 1a894bfe Christos Stavrakakis
784 1a894bfe Christos Stavrakakis
785 e77a29ab Christos Stavrakakis
def get_jobs(backend):
786 e77a29ab Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
787 e77a29ab Christos Stavrakakis
        return c.GetJobs()
788 f5b4f2a3 Christos Stavrakakis
789 17852fe9 Giorgos Verigakis
790 1a894bfe Christos Stavrakakis
def get_physical_resources(backend):
791 1a894bfe Christos Stavrakakis
    """ Get the physical resources of a backend.
792 1a894bfe Christos Stavrakakis

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

795 1a894bfe Christos Stavrakakis
    """
796 e77a29ab Christos Stavrakakis
    nodes = get_nodes(backend, bulk=True)
797 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
798 1a894bfe Christos Stavrakakis
    res = {}
799 1a894bfe Christos Stavrakakis
    for a in attr:
800 1a894bfe Christos Stavrakakis
        res[a] = 0
801 1a894bfe Christos Stavrakakis
    for n in nodes:
802 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
803 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
804 cc92b70f Christos Stavrakakis
        can_host_vms = n['vm_capable'] and not (n['drained'] or n['offline'])
805 cc92b70f Christos Stavrakakis
        if can_host_vms and n['cnodes']:
806 1a894bfe Christos Stavrakakis
            for a in attr:
807 1a894bfe Christos Stavrakakis
                res[a] += int(n[a])
808 1a894bfe Christos Stavrakakis
    return res
809 1a894bfe Christos Stavrakakis
810 1a894bfe Christos Stavrakakis
811 1a894bfe Christos Stavrakakis
def update_resources(backend, resources=None):
812 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
813 1a894bfe Christos Stavrakakis

814 1a894bfe Christos Stavrakakis
    """
815 17852fe9 Giorgos Verigakis
816 1a894bfe Christos Stavrakakis
    if not resources:
817 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
818 41303ed0 Vangelis Koukis
819 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
820 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
821 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
822 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
823 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
824 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
825 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
826 1a894bfe Christos Stavrakakis
    backend.save()
827 1a894bfe Christos Stavrakakis
828 1a894bfe Christos Stavrakakis
829 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
830 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
831 1a894bfe Christos Stavrakakis

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

835 1a894bfe Christos Stavrakakis
    """
836 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
837 3524241a Christos Stavrakakis
        instances = client.GetInstances(bulk=True)
838 1a894bfe Christos Stavrakakis
    mem = 0
839 1a894bfe Christos Stavrakakis
    for i in instances:
840 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
841 1a894bfe Christos Stavrakakis
    return mem
842 b3d28af2 Christos Stavrakakis
843 b3d28af2 Christos Stavrakakis
##
844 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
845 b3d28af2 Christos Stavrakakis
##
846 b3d28af2 Christos Stavrakakis
847 b3d28af2 Christos Stavrakakis
848 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
849 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
850 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
851 b3d28af2 Christos Stavrakakis
        return result
852 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
853 b3d28af2 Christos Stavrakakis
    return result
854 b3d28af2 Christos Stavrakakis
855 b3d28af2 Christos Stavrakakis
856 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
857 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
858 e60b4630 Christos Stavrakakis
        job = _create_network(network, backend)
859 3524241a Christos Stavrakakis
        result = wait_for_job(client, job)
860 3524241a Christos Stavrakakis
    return result
861 b3d28af2 Christos Stavrakakis
862 b3d28af2 Christos Stavrakakis
863 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
864 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as client:
865 3524241a Christos Stavrakakis
        for group in client.GetGroups():
866 b7d38981 Dimitris Aragiorgis
            job = client.ConnectNetwork(network.backend_id, group,
867 b7d38981 Dimitris Aragiorgis
                                        network.mode, network.link)
868 3524241a Christos Stavrakakis
            result = wait_for_job(client, job)
869 3524241a Christos Stavrakakis
            if result[0] != 'success':
870 3524241a Christos Stavrakakis
                return result
871 b3d28af2 Christos Stavrakakis
872 b3d28af2 Christos Stavrakakis
    return result
873 b3d28af2 Christos Stavrakakis
874 b3d28af2 Christos Stavrakakis
875 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
876 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
877 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
878 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
879 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
880 cc92b70f Christos Stavrakakis
                                         [result], None)
881 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
882 b3d28af2 Christos Stavrakakis
883 b3d28af2 Christos Stavrakakis
    if status == 'success':
884 b3d28af2 Christos Stavrakakis
        return (status, None)
885 b3d28af2 Christos Stavrakakis
    else:
886 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
887 b3d28af2 Christos Stavrakakis
        return (status, error)