Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (20.8 kB)

1 adee02b8 Giorgos Verigakis
# Copyright 2011 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 02feca11 Vassilios Karakoidas
34 2b1db26f Giorgos Verigakis
import json
35 2b1db26f Giorgos Verigakis
36 2035039b Giorgos Verigakis
from logging import getLogger
37 529178b1 Giorgos Verigakis
from django.conf import settings
38 207b70d5 Giorgos Verigakis
from django.db import transaction
39 1a894bfe Christos Stavrakakis
from datetime import datetime
40 207b70d5 Giorgos Verigakis
41 22ee6892 Christos Stavrakakis
from synnefo.db.models import (Backend, VirtualMachine, Network,
42 22ee6892 Christos Stavrakakis
                               BackendNetwork, BACKEND_STATUSES)
43 234f8b07 Vangelis Koukis
from synnefo.logic import utils
44 529178b1 Giorgos Verigakis
from synnefo.util.rapi import GanetiRapiClient
45 529178b1 Giorgos Verigakis
46 9e98ba3c Giorgos Verigakis
log = getLogger('synnefo.logic')
47 9e98ba3c Giorgos Verigakis
48 529178b1 Giorgos Verigakis
49 efff6193 Giorgos Verigakis
_firewall_tags = {
50 efff6193 Giorgos Verigakis
    'ENABLED': settings.GANETI_FIREWALL_ENABLED_TAG,
51 efff6193 Giorgos Verigakis
    'DISABLED': settings.GANETI_FIREWALL_DISABLED_TAG,
52 efff6193 Giorgos Verigakis
    'PROTECTED': settings.GANETI_FIREWALL_PROTECTED_TAG}
53 efff6193 Giorgos Verigakis
54 efff6193 Giorgos Verigakis
_reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
55 efff6193 Giorgos Verigakis
56 02feca11 Vassilios Karakoidas
57 924d8085 Christos Stavrakakis
def create_client(hostname, port=5080, username=None, password=None):
58 1a894bfe Christos Stavrakakis
    return GanetiRapiClient(hostname, port, username, password)
59 924d8085 Christos Stavrakakis
60 22ee6892 Christos Stavrakakis
61 093f9c53 Vangelis Koukis
@transaction.commit_on_success
62 c4e55622 Christos Stavrakakis
def process_op_status(vm, etime, jobid, opcode, status, logmsg):
63 ad2d6807 Vangelis Koukis
    """Process a job progress notification from the backend
64 02feca11 Vassilios Karakoidas

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

69 02feca11 Vassilios Karakoidas
    """
70 41303ed0 Vangelis Koukis
    # See #1492, #1031, #1111 why this line has been removed
71 41303ed0 Vangelis Koukis
    #if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
72 fd65ab41 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
73 02feca11 Vassilios Karakoidas
        raise VirtualMachine.InvalidBackendMsgError(opcode, status)
74 02feca11 Vassilios Karakoidas
75 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = jobid
76 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = status
77 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = opcode
78 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = logmsg
79 02feca11 Vassilios Karakoidas
80 02feca11 Vassilios Karakoidas
    # Notifications of success change the operating state
81 41303ed0 Vangelis Koukis
    state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode, None)
82 41303ed0 Vangelis Koukis
    if status == 'success' and state_for_success is not None:
83 41303ed0 Vangelis Koukis
        utils.update_state(vm, state_for_success)
84 41303ed0 Vangelis Koukis
        # Set the deleted flag explicitly, cater for admin-initiated removals
85 685b219e Vangelis Koukis
        if opcode == 'OP_INSTANCE_REMOVE':
86 685b219e Vangelis Koukis
            vm.deleted = True
87 9c21a2e2 Christos Stavrakakis
            vm.nics.all().delete()
88 685b219e Vangelis Koukis
89 685b219e Vangelis Koukis
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
90 02feca11 Vassilios Karakoidas
    if status in ('canceled', 'error') and opcode == 'OP_INSTANCE_CREATE':
91 234f8b07 Vangelis Koukis
        utils.update_state(vm, 'ERROR')
92 093f9c53 Vangelis Koukis
93 093f9c53 Vangelis Koukis
    # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR,
94 093f9c53 Vangelis Koukis
    # when no instance exists at the Ganeti backend.
95 093f9c53 Vangelis Koukis
    # See ticket #799 for all the details.
96 093f9c53 Vangelis Koukis
    #
97 093f9c53 Vangelis Koukis
    if (status == 'error' and opcode == 'OP_INSTANCE_REMOVE' and
98 093f9c53 Vangelis Koukis
        vm.operstate == 'ERROR'):
99 093f9c53 Vangelis Koukis
        vm.deleted = True
100 9c21a2e2 Christos Stavrakakis
        vm.nics.all().delete()
101 093f9c53 Vangelis Koukis
102 c4e55622 Christos Stavrakakis
    vm.backendtime = etime
103 02feca11 Vassilios Karakoidas
    # Any other notification of failure leaves the operating state unchanged
104 02feca11 Vassilios Karakoidas
105 02feca11 Vassilios Karakoidas
    vm.save()
106 22e52ede Vassilios Karakoidas
107 ad2d6807 Vangelis Koukis
108 207b70d5 Giorgos Verigakis
@transaction.commit_on_success
109 c4e55622 Christos Stavrakakis
def process_net_status(vm, etime, nics):
110 ad2d6807 Vangelis Koukis
    """Process a net status notification from the backend
111 ad2d6807 Vangelis Koukis

112 ad2d6807 Vangelis Koukis
    Process an incoming message from the Ganeti backend,
113 ad2d6807 Vangelis Koukis
    detailing the NIC configuration of a VM instance.
114 ad2d6807 Vangelis Koukis

115 ad2d6807 Vangelis Koukis
    Update the state of the VM in the DB accordingly.
116 ad2d6807 Vangelis Koukis
    """
117 37ca953f Christodoulos Psaltis
118 64938cb0 Giorgos Verigakis
    vm.nics.all().delete()
119 64938cb0 Giorgos Verigakis
    for i, nic in enumerate(nics):
120 22ee6892 Christos Stavrakakis
        network = nic.get('network', '')
121 22ee6892 Christos Stavrakakis
        n = str(network)
122 22ee6892 Christos Stavrakakis
        if n == settings.GANETI_PUBLIC_NETWORK:
123 ca792e04 Giorgos Verigakis
            net = Network.objects.get(public=True)
124 64938cb0 Giorgos Verigakis
        else:
125 22ee6892 Christos Stavrakakis
            pk = utils.id_from_network_name(n)
126 22ee6892 Christos Stavrakakis
            net = Network.objects.get(id=pk)
127 9afeb669 Kostas Papadimitriou
128 658a825a Giorgos Verigakis
        firewall = nic.get('firewall', '')
129 658a825a Giorgos Verigakis
        firewall_profile = _reverse_tags.get(firewall, '')
130 658a825a Giorgos Verigakis
        if not firewall_profile and net.public:
131 658a825a Giorgos Verigakis
            firewall_profile = settings.DEFAULT_FIREWALL_PROFILE
132 9afeb669 Kostas Papadimitriou
133 64938cb0 Giorgos Verigakis
        vm.nics.create(
134 64938cb0 Giorgos Verigakis
            network=net,
135 64938cb0 Giorgos Verigakis
            index=i,
136 64938cb0 Giorgos Verigakis
            mac=nic.get('mac', ''),
137 746c6bf4 Vangelis Koukis
            ipv4=nic.get('ip', ''),
138 41303ed0 Vangelis Koukis
            ipv6=nic.get('ipv6', ''),
139 efff6193 Giorgos Verigakis
            firewall_profile=firewall_profile)
140 9afeb669 Kostas Papadimitriou
141 c4e55622 Christos Stavrakakis
    vm.backendtime = etime
142 ad2d6807 Vangelis Koukis
    vm.save()
143 22ee6892 Christos Stavrakakis
    net.save()
144 22ee6892 Christos Stavrakakis
145 22ee6892 Christos Stavrakakis
146 22ee6892 Christos Stavrakakis
@transaction.commit_on_success
147 22ee6892 Christos Stavrakakis
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
148 22ee6892 Christos Stavrakakis
    if status not in [x[0] for x in BACKEND_STATUSES]:
149 22ee6892 Christos Stavrakakis
        return
150 22ee6892 Christos Stavrakakis
        #raise Network.InvalidBackendMsgError(opcode, status)
151 22ee6892 Christos Stavrakakis
152 22ee6892 Christos Stavrakakis
    back_network.backendjobid = jobid
153 22ee6892 Christos Stavrakakis
    back_network.backendjobstatus = status
154 22ee6892 Christos Stavrakakis
    back_network.backendopcode = opcode
155 22ee6892 Christos Stavrakakis
    back_network.backendlogmsg = logmsg
156 22ee6892 Christos Stavrakakis
157 22ee6892 Christos Stavrakakis
    # Notifications of success change the operating state
158 22ee6892 Christos Stavrakakis
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
159 22ee6892 Christos Stavrakakis
    if status == 'success' and state_for_success is not None:
160 22ee6892 Christos Stavrakakis
        back_network.operstate = state_for_success
161 22ee6892 Christos Stavrakakis
        if opcode == 'OP_NETWORK_REMOVE':
162 22ee6892 Christos Stavrakakis
            back_network.deleted = True
163 22ee6892 Christos Stavrakakis
164 22ee6892 Christos Stavrakakis
    if status in ('canceled', 'error'):
165 22ee6892 Christos Stavrakakis
        utils.update_state(back_network, 'ERROR')
166 22ee6892 Christos Stavrakakis
167 22ee6892 Christos Stavrakakis
    if (status == 'error' and opcode == 'OP_NETWORK_REMOVE' and
168 22ee6892 Christos Stavrakakis
        back_network.operstate == 'ERROR'):
169 22ee6892 Christos Stavrakakis
        back_network.deleted = True
170 22ee6892 Christos Stavrakakis
        back_network.operstate = 'DELETED'
171 22ee6892 Christos Stavrakakis
172 22ee6892 Christos Stavrakakis
    back_network.save()
173 ad2d6807 Vangelis Koukis
174 c25cc9ec Vangelis Koukis
175 9068cd85 Georgios Gousios
@transaction.commit_on_success
176 c4e55622 Christos Stavrakakis
def process_create_progress(vm, etime, rprogress, wprogress):
177 9068cd85 Georgios Gousios
178 c25cc9ec Vangelis Koukis
    # XXX: This only uses the read progress for now.
179 c25cc9ec Vangelis Koukis
    #      Explore whether it would make sense to use the value of wprogress
180 c25cc9ec Vangelis Koukis
    #      somewhere.
181 c25cc9ec Vangelis Koukis
    percentage = int(rprogress)
182 9068cd85 Georgios Gousios
183 af90d919 Vangelis Koukis
    # The percentage may exceed 100%, due to the way
184 af90d919 Vangelis Koukis
    # snf-progress-monitor tracks bytes read by image handling processes
185 af90d919 Vangelis Koukis
    percentage = 100 if percentage > 100 else percentage
186 af90d919 Vangelis Koukis
    if percentage < 0:
187 af90d919 Vangelis Koukis
        raise ValueError("Percentage cannot be negative")
188 9068cd85 Georgios Gousios
189 7ec9558b Vangelis Koukis
    # FIXME: log a warning here, see #1033
190 7ec9558b Vangelis Koukis
#   if last_update > percentage:
191 7ec9558b Vangelis Koukis
#       raise ValueError("Build percentage should increase monotonically " \
192 7ec9558b Vangelis Koukis
#                        "(old = %d, new = %d)" % (last_update, percentage))
193 9068cd85 Georgios Gousios
194 c25cc9ec Vangelis Koukis
    # This assumes that no message of type 'ganeti-create-progress' is going to
195 c25cc9ec Vangelis Koukis
    # arrive once OP_INSTANCE_CREATE has succeeded for a Ganeti instance and
196 c25cc9ec Vangelis Koukis
    # the instance is STARTED.  What if the two messages are processed by two
197 c25cc9ec Vangelis Koukis
    # separate dispatcher threads, and the 'ganeti-op-status' message for
198 c25cc9ec Vangelis Koukis
    # successful creation gets processed before the 'ganeti-create-progress'
199 c25cc9ec Vangelis Koukis
    # message? [vkoukis]
200 c25cc9ec Vangelis Koukis
    #
201 c25cc9ec Vangelis Koukis
    #if not vm.operstate == 'BUILD':
202 c25cc9ec Vangelis Koukis
    #    raise VirtualMachine.IllegalState("VM is not in building state")
203 9068cd85 Georgios Gousios
204 c25cc9ec Vangelis Koukis
    vm.buildpercentage = percentage
205 c4e55622 Christos Stavrakakis
    vm.backendtime = etime
206 9068cd85 Georgios Gousios
    vm.save()
207 ad2d6807 Vangelis Koukis
208 c25cc9ec Vangelis Koukis
209 22e52ede Vassilios Karakoidas
def start_action(vm, action):
210 22e52ede Vassilios Karakoidas
    """Update the state of a VM when a new action is initiated."""
211 22e52ede Vassilios Karakoidas
    if not action in [x[0] for x in VirtualMachine.ACTIONS]:
212 22e52ede Vassilios Karakoidas
        raise VirtualMachine.InvalidActionError(action)
213 22e52ede Vassilios Karakoidas
214 22e52ede Vassilios Karakoidas
    # No actions to deleted and no actions beside destroy to suspended VMs
215 22e52ede Vassilios Karakoidas
    if vm.deleted:
216 5231a38a Giorgos Verigakis
        raise VirtualMachine.DeletedError
217 37ca953f Christodoulos Psaltis
218 30f3b5e5 Vangelis Koukis
    # No actions to machines being built. They may be destroyed, however.
219 30f3b5e5 Vangelis Koukis
    if vm.operstate == 'BUILD' and action != 'DESTROY':
220 5231a38a Giorgos Verigakis
        raise VirtualMachine.BuildingError
221 37ca953f Christodoulos Psaltis
222 dfd19c2d Vassilios Karakoidas
    vm.action = action
223 dfd19c2d Vassilios Karakoidas
    vm.backendjobid = None
224 dfd19c2d Vassilios Karakoidas
    vm.backendopcode = None
225 dfd19c2d Vassilios Karakoidas
    vm.backendjobstatus = None
226 dfd19c2d Vassilios Karakoidas
    vm.backendlogmsg = None
227 22e52ede Vassilios Karakoidas
228 f90c3d8c Vangelis Koukis
    # Update the relevant flags if the VM is being suspended or destroyed.
229 f90c3d8c Vangelis Koukis
    # Do not set the deleted flag here, see ticket #721.
230 f90c3d8c Vangelis Koukis
    #
231 f90c3d8c Vangelis Koukis
    # The deleted flag is set asynchronously, when an OP_INSTANCE_REMOVE
232 f90c3d8c Vangelis Koukis
    # completes successfully. Hence, a server may be visible for some time
233 f90c3d8c Vangelis Koukis
    # after a DELETE /servers/id returns HTTP 204.
234 f90c3d8c Vangelis Koukis
    #
235 22e52ede Vassilios Karakoidas
    if action == "DESTROY":
236 f90c3d8c Vangelis Koukis
        # vm.deleted = True
237 f90c3d8c Vangelis Koukis
        pass
238 22e52ede Vassilios Karakoidas
    elif action == "SUSPEND":
239 22e52ede Vassilios Karakoidas
        vm.suspended = True
240 22e52ede Vassilios Karakoidas
    elif action == "START":
241 22e52ede Vassilios Karakoidas
        vm.suspended = False
242 22e52ede Vassilios Karakoidas
    vm.save()
243 529178b1 Giorgos Verigakis
244 ad2d6807 Vangelis Koukis
245 2b1db26f Giorgos Verigakis
def create_instance(vm, flavor, image, password, personality):
246 3a9b3cde Giorgos Verigakis
    """`image` is a dictionary which should contain the keys:
247 3a9b3cde Giorgos Verigakis
            'backend_id', 'format' and 'metadata'
248 7f691719 Christos Stavrakakis

249 3a9b3cde Giorgos Verigakis
        metadata value should be a dictionary.
250 3a9b3cde Giorgos Verigakis
    """
251 7f691719 Christos Stavrakakis
    nic = {'ip': 'pool', 'network': settings.GANETI_PUBLIC_NETWORK}
252 61868190 Vangelis Koukis
253 61868190 Vangelis Koukis
    if settings.IGNORE_FLAVOR_DISK_SIZES:
254 3a9b3cde Giorgos Verigakis
        if image['backend_id'].find("windows") >= 0:
255 61868190 Vangelis Koukis
            sz = 14000
256 61868190 Vangelis Koukis
        else:
257 61868190 Vangelis Koukis
            sz = 4000
258 cf0e4232 Vangelis Koukis
    else:
259 61868190 Vangelis Koukis
        sz = flavor.disk * 1024
260 37ca953f Christodoulos Psaltis
261 1c382247 Vangelis Koukis
    # Handle arguments to CreateInstance() as a dictionary,
262 1c382247 Vangelis Koukis
    # initialize it based on a deployment-specific value.
263 1c382247 Vangelis Koukis
    # This enables the administrator to override deployment-specific
264 c25cc9ec Vangelis Koukis
    # arguments, such as the disk template to use, name of os provider
265 1c382247 Vangelis Koukis
    # and hypervisor-specific parameters at will (see Synnefo #785, #835).
266 1c382247 Vangelis Koukis
    #
267 1c382247 Vangelis Koukis
    kw = settings.GANETI_CREATEINSTANCE_KWARGS
268 1c382247 Vangelis Koukis
    kw['mode'] = 'create'
269 924d8085 Christos Stavrakakis
    kw['name'] = vm.backend_vm_id
270 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
271 805c79ae Giorgos Verigakis
    kw['disk_template'] = flavor.disk_template
272 1c382247 Vangelis Koukis
    kw['disks'] = [{"size": sz}]
273 1c382247 Vangelis Koukis
    kw['nics'] = [nic]
274 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
275 1c382247 Vangelis Koukis
    # kw['os'] = settings.GANETI_OS_PROVIDER
276 1c382247 Vangelis Koukis
    kw['ip_check'] = False
277 1c382247 Vangelis Koukis
    kw['name_check'] = False
278 1c382247 Vangelis Koukis
    # Do not specific a node explicitly, have
279 1c382247 Vangelis Koukis
    # Ganeti use an iallocator instead
280 1c382247 Vangelis Koukis
    #
281 1c382247 Vangelis Koukis
    # kw['pnode']=rapi.GetNodes()[0]
282 1c382247 Vangelis Koukis
    kw['dry_run'] = settings.TEST
283 41303ed0 Vangelis Koukis
284 2b1db26f Giorgos Verigakis
    kw['beparams'] = {
285 2b1db26f Giorgos Verigakis
        'auto_balance': True,
286 2b1db26f Giorgos Verigakis
        'vcpus': flavor.cpu,
287 2b1db26f Giorgos Verigakis
        'memory': flavor.ram}
288 41303ed0 Vangelis Koukis
289 e3b5be49 Giorgos Verigakis
    kw['osparams'] = {
290 3a9b3cde Giorgos Verigakis
        'img_id': image['backend_id'],
291 e3b5be49 Giorgos Verigakis
        'img_passwd': password,
292 3a9b3cde Giorgos Verigakis
        'img_format': image['format']}
293 2b1db26f Giorgos Verigakis
    if personality:
294 e3b5be49 Giorgos Verigakis
        kw['osparams']['img_personality'] = json.dumps(personality)
295 d1eaa651 Christos Stavrakakis
296 3a9b3cde Giorgos Verigakis
    kw['osparams']['img_properties'] = json.dumps(image['metadata'])
297 d1eaa651 Christos Stavrakakis
298 5949b704 Vangelis Koukis
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
299 1c382247 Vangelis Koukis
    # kw['hvparams'] = dict(serial_console=False)
300 1c382247 Vangelis Koukis
301 f5b4f2a3 Christos Stavrakakis
    return vm.client.CreateInstance(**kw)
302 f533f224 Vangelis Koukis
303 529178b1 Giorgos Verigakis
304 529178b1 Giorgos Verigakis
def delete_instance(vm):
305 529178b1 Giorgos Verigakis
    start_action(vm, 'DESTROY')
306 f5b4f2a3 Christos Stavrakakis
    vm.client.DeleteInstance(vm.backend_vm_id, dry_run=settings.TEST)
307 529178b1 Giorgos Verigakis
308 ad2d6807 Vangelis Koukis
309 529178b1 Giorgos Verigakis
def reboot_instance(vm, reboot_type):
310 529178b1 Giorgos Verigakis
    assert reboot_type in ('soft', 'hard')
311 f5b4f2a3 Christos Stavrakakis
    vm.client.RebootInstance(vm.backend_vm_id, reboot_type, dry_run=settings.TEST)
312 924d8085 Christos Stavrakakis
    log.info('Rebooting instance %s', vm.backend_vm_id)
313 529178b1 Giorgos Verigakis
314 ad2d6807 Vangelis Koukis
315 529178b1 Giorgos Verigakis
def startup_instance(vm):
316 529178b1 Giorgos Verigakis
    start_action(vm, 'START')
317 f5b4f2a3 Christos Stavrakakis
    vm.client.StartupInstance(vm.backend_vm_id, dry_run=settings.TEST)
318 529178b1 Giorgos Verigakis
319 ad2d6807 Vangelis Koukis
320 529178b1 Giorgos Verigakis
def shutdown_instance(vm):
321 529178b1 Giorgos Verigakis
    start_action(vm, 'STOP')
322 f5b4f2a3 Christos Stavrakakis
    vm.client.ShutdownInstance(vm.backend_vm_id, dry_run=settings.TEST)
323 529178b1 Giorgos Verigakis
324 ad2d6807 Vangelis Koukis
325 529178b1 Giorgos Verigakis
def get_instance_console(vm):
326 71099804 Vangelis Koukis
    # RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
327 71099804 Vangelis Koukis
    # which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
328 71099804 Vangelis Koukis
    # useless (see #783).
329 71099804 Vangelis Koukis
    #
330 71099804 Vangelis Koukis
    # Until this is fixed on the Ganeti side, construct a console info reply
331 71099804 Vangelis Koukis
    # directly.
332 9afeb669 Kostas Papadimitriou
    #
333 71099804 Vangelis Koukis
    # WARNING: This assumes that VNC runs on port network_port on
334 71099804 Vangelis Koukis
    #          the instance's primary node, and is probably
335 71099804 Vangelis Koukis
    #          hypervisor-specific.
336 71099804 Vangelis Koukis
    #
337 71099804 Vangelis Koukis
    console = {}
338 71099804 Vangelis Koukis
    console['kind'] = 'vnc'
339 f5b4f2a3 Christos Stavrakakis
    i = vm.client.GetInstance(vm.backend_vm_id)
340 71099804 Vangelis Koukis
    if i['hvparams']['serial_console']:
341 71099804 Vangelis Koukis
        raise Exception("hv parameter serial_console cannot be true")
342 71099804 Vangelis Koukis
    console['host'] = i['pnode']
343 71099804 Vangelis Koukis
    console['port'] = i['network_port']
344 9afeb669 Kostas Papadimitriou
345 71099804 Vangelis Koukis
    return console
346 924d8085 Christos Stavrakakis
    # return rapi.GetInstanceConsole(vm.backend_vm_id)
347 64938cb0 Giorgos Verigakis
348 c25cc9ec Vangelis Koukis
349 604b2bf8 Georgios Gousios
def request_status_update(vm):
350 f5b4f2a3 Christos Stavrakakis
    return vm.client.GetInstanceInfo(vm.backend_vm_id)
351 604b2bf8 Georgios Gousios
352 604b2bf8 Georgios Gousios
353 604b2bf8 Georgios Gousios
def update_status(vm, status):
354 db7a3230 Vangelis Koukis
    utils.update_state(vm, status)
355 f533f224 Vangelis Koukis
356 c25cc9ec Vangelis Koukis
357 22ee6892 Christos Stavrakakis
def create_network(network, backends=None):
358 22ee6892 Christos Stavrakakis
    """ Add and connect a network to backends.
359 37ca953f Christodoulos Psaltis

360 22ee6892 Christos Stavrakakis
    @param network: Network object
361 22ee6892 Christos Stavrakakis
    @param backends: List of Backend objects. None defaults to all.
362 64938cb0 Giorgos Verigakis

363 22ee6892 Christos Stavrakakis
    """
364 22ee6892 Christos Stavrakakis
    backend_jobs = _create_network(network, backends)
365 22ee6892 Christos Stavrakakis
    connect_network(network, backend_jobs)
366 22ee6892 Christos Stavrakakis
    return network
367 c25cc9ec Vangelis Koukis
368 37ca953f Christodoulos Psaltis
369 22ee6892 Christos Stavrakakis
def _create_network(network, backends=None):
370 22ee6892 Christos Stavrakakis
    """Add a network to backends.
371 22ee6892 Christos Stavrakakis
    @param network: Network object
372 22ee6892 Christos Stavrakakis
    @param backends: List of Backend objects. None defaults to all.
373 64938cb0 Giorgos Verigakis

374 22ee6892 Christos Stavrakakis
    """
375 c25cc9ec Vangelis Koukis
376 22ee6892 Christos Stavrakakis
    network_type = network.public and 'public' or 'private'
377 22ee6892 Christos Stavrakakis
378 22ee6892 Christos Stavrakakis
    if not backends:
379 22ee6892 Christos Stavrakakis
        backends = Backend.objects.exclude(offline=True)
380 22ee6892 Christos Stavrakakis
381 22ee6892 Christos Stavrakakis
    tags = network.backend_tag
382 22ee6892 Christos Stavrakakis
    if network.dhcp:
383 22ee6892 Christos Stavrakakis
        tags.append('nfdhcpd')
384 22ee6892 Christos Stavrakakis
    tags = ','.join(tags)
385 22ee6892 Christos Stavrakakis
386 22ee6892 Christos Stavrakakis
    backend_jobs = []
387 22ee6892 Christos Stavrakakis
    for backend in backends:
388 22ee6892 Christos Stavrakakis
        job = backend.client.CreateNetwork(
389 22ee6892 Christos Stavrakakis
                network_name=network.backend_id,
390 22ee6892 Christos Stavrakakis
                network=network.subnet,
391 22ee6892 Christos Stavrakakis
                gateway=network.gateway,
392 22ee6892 Christos Stavrakakis
                network_type=network_type,
393 22ee6892 Christos Stavrakakis
                mac_prefix=network.mac_prefix,
394 22ee6892 Christos Stavrakakis
                tags=tags)
395 22ee6892 Christos Stavrakakis
        backend_jobs.append((backend, job))
396 22ee6892 Christos Stavrakakis
397 22ee6892 Christos Stavrakakis
    return backend_jobs
398 22ee6892 Christos Stavrakakis
399 22ee6892 Christos Stavrakakis
400 22ee6892 Christos Stavrakakis
def connect_network(network, backend_jobs=None):
401 22ee6892 Christos Stavrakakis
    """Connect a network to all nodegroups.
402 22ee6892 Christos Stavrakakis

403 22ee6892 Christos Stavrakakis
    @param network: Network object
404 22ee6892 Christos Stavrakakis
    @param backend_jobs: List of tuples of the form (Backend, jobs) which are
405 22ee6892 Christos Stavrakakis
                         the backends to connect the network and the jobs on
406 22ee6892 Christos Stavrakakis
                         which the connect job depends.
407 22ee6892 Christos Stavrakakis

408 22ee6892 Christos Stavrakakis
    """
409 64938cb0 Giorgos Verigakis
410 22ee6892 Christos Stavrakakis
    mode = network.public and 'routed' or 'bridged'
411 c25cc9ec Vangelis Koukis
412 22ee6892 Christos Stavrakakis
    if not backend_jobs:
413 22ee6892 Christos Stavrakakis
        backend_jobs = [(backend, []) for backend in
414 22ee6892 Christos Stavrakakis
                        Backend.objects.exclude(offline=True)]
415 64938cb0 Giorgos Verigakis
416 22ee6892 Christos Stavrakakis
    for backend, job in backend_jobs:
417 22ee6892 Christos Stavrakakis
        client = backend.client
418 22ee6892 Christos Stavrakakis
        for group in client.GetGroups():
419 22ee6892 Christos Stavrakakis
            client.ConnectNetwork(network.backend_id, group, mode,
420 22ee6892 Christos Stavrakakis
                                  network.link, [job])
421 22ee6892 Christos Stavrakakis
422 22ee6892 Christos Stavrakakis
423 22ee6892 Christos Stavrakakis
def connect_network_group(backend, network, group):
424 22ee6892 Christos Stavrakakis
    """Connect a network to a specific nodegroup of a backend.
425 22ee6892 Christos Stavrakakis

426 22ee6892 Christos Stavrakakis
    """
427 22ee6892 Christos Stavrakakis
    mode = network.public and 'routed' or 'bridged'
428 22ee6892 Christos Stavrakakis
429 22ee6892 Christos Stavrakakis
    return backend.client.ConnectNetwork(network.backend_id, group, mode,
430 22ee6892 Christos Stavrakakis
                                         network.link)
431 22ee6892 Christos Stavrakakis
432 22ee6892 Christos Stavrakakis
433 22ee6892 Christos Stavrakakis
def delete_network(network, backends=None):
434 22ee6892 Christos Stavrakakis
    """ Disconnect and a remove a network from backends.
435 22ee6892 Christos Stavrakakis

436 22ee6892 Christos Stavrakakis
    @param network: Network object
437 22ee6892 Christos Stavrakakis
    @param backends: List of Backend objects. None defaults to all.
438 22ee6892 Christos Stavrakakis

439 22ee6892 Christos Stavrakakis
    """
440 22ee6892 Christos Stavrakakis
    backend_jobs = disconnect_network(network, backends)
441 22ee6892 Christos Stavrakakis
    _delete_network(network, backend_jobs)
442 22ee6892 Christos Stavrakakis
443 22ee6892 Christos Stavrakakis
444 22ee6892 Christos Stavrakakis
def disconnect_network(network, backends=None):
445 22ee6892 Christos Stavrakakis
    """Disconnect a network from virtualmachines and nodegroups.
446 22ee6892 Christos Stavrakakis

447 22ee6892 Christos Stavrakakis
    @param network: Network object
448 22ee6892 Christos Stavrakakis
    @param backends: List of Backend objects. None defaults to all.
449 22ee6892 Christos Stavrakakis

450 22ee6892 Christos Stavrakakis
    """
451 22ee6892 Christos Stavrakakis
452 22ee6892 Christos Stavrakakis
    if not backends:
453 22ee6892 Christos Stavrakakis
        backends = Backend.objects.exclude(offline=True)
454 22ee6892 Christos Stavrakakis
455 22ee6892 Christos Stavrakakis
    backend_jobs = []
456 22ee6892 Christos Stavrakakis
    for backend in backends:
457 22ee6892 Christos Stavrakakis
        client = backend.client
458 22ee6892 Christos Stavrakakis
        jobs = []
459 22ee6892 Christos Stavrakakis
        for vm in network.machines.filter(backend=backend):
460 22ee6892 Christos Stavrakakis
            job = disconnect_from_network(vm, network)
461 22ee6892 Christos Stavrakakis
            jobs.append(job)
462 22ee6892 Christos Stavrakakis
463 22ee6892 Christos Stavrakakis
        jobs2 = []
464 22ee6892 Christos Stavrakakis
        for group in client.GetGroups():
465 22ee6892 Christos Stavrakakis
            job = client.DisconnectNetwork(network.backend_id, group, jobs)
466 22ee6892 Christos Stavrakakis
            jobs2.append(job)
467 22ee6892 Christos Stavrakakis
        backend_jobs.append((backend, jobs2))
468 22ee6892 Christos Stavrakakis
469 22ee6892 Christos Stavrakakis
    return backend_jobs
470 22ee6892 Christos Stavrakakis
471 22ee6892 Christos Stavrakakis
472 22ee6892 Christos Stavrakakis
def disconnect_from_network(vm, network):
473 22ee6892 Christos Stavrakakis
    """Disconnect a virtual machine from a network by removing it's nic.
474 22ee6892 Christos Stavrakakis

475 22ee6892 Christos Stavrakakis
    @param vm: VirtualMachine object
476 22ee6892 Christos Stavrakakis
    @param network: Network object
477 22ee6892 Christos Stavrakakis

478 22ee6892 Christos Stavrakakis
    """
479 c25cc9ec Vangelis Koukis
480 ca792e04 Giorgos Verigakis
    nics = vm.nics.filter(network__public=False).order_by('index')
481 22ee6892 Christos Stavrakakis
    ops = [('remove', nic.index, {}) for nic in nics if nic.network == network]
482 924d8085 Christos Stavrakakis
    if not ops:  # Vm not connected to network
483 d1eaa651 Christos Stavrakakis
        return
484 22ee6892 Christos Stavrakakis
    job = vm.client.ModifyInstance(vm.backend_vm_id, nics=ops[::-1],
485 22ee6892 Christos Stavrakakis
                                    hotplug=True, dry_run=settings.TEST)
486 22ee6892 Christos Stavrakakis
487 22ee6892 Christos Stavrakakis
    return job
488 22ee6892 Christos Stavrakakis
489 22ee6892 Christos Stavrakakis
490 22ee6892 Christos Stavrakakis
def _delete_network(network, backend_jobs=None):
491 22ee6892 Christos Stavrakakis
    if not backend_jobs:
492 22ee6892 Christos Stavrakakis
        backend_jobs = [(backend, []) for backend in
493 22ee6892 Christos Stavrakakis
                Backend.objects.exclude(offline=True)]
494 22ee6892 Christos Stavrakakis
    for backend, jobs in backend_jobs:
495 22ee6892 Christos Stavrakakis
        backend.client.DeleteNetwork(network.backend_id, jobs)
496 22ee6892 Christos Stavrakakis
497 22ee6892 Christos Stavrakakis
498 22ee6892 Christos Stavrakakis
def connect_to_network(vm, network):
499 22ee6892 Christos Stavrakakis
    """Connect a virtual machine to a network.
500 22ee6892 Christos Stavrakakis

501 22ee6892 Christos Stavrakakis
    @param vm: VirtualMachine object
502 22ee6892 Christos Stavrakakis
    @param network: Network object
503 22ee6892 Christos Stavrakakis

504 22ee6892 Christos Stavrakakis
    """
505 22ee6892 Christos Stavrakakis
506 22ee6892 Christos Stavrakakis
    ip = network.dhcp and 'pool' or None
507 22ee6892 Christos Stavrakakis
508 22ee6892 Christos Stavrakakis
    nic = {'ip': ip, 'network': network.backend_id}
509 22ee6892 Christos Stavrakakis
    vm.client.ModifyInstance(vm.backend_vm_id, nics=[('add',  nic)],
510 22ee6892 Christos Stavrakakis
                             hotplug=True, dry_run=settings.TEST)
511 91826390 Giorgos Verigakis
512 c25cc9ec Vangelis Koukis
513 91826390 Giorgos Verigakis
def set_firewall_profile(vm, profile):
514 26563957 Giorgos Verigakis
    try:
515 26563957 Giorgos Verigakis
        tag = _firewall_tags[profile]
516 26563957 Giorgos Verigakis
    except KeyError:
517 91826390 Giorgos Verigakis
        raise ValueError("Unsopported Firewall Profile: %s" % profile)
518 37ca953f Christodoulos Psaltis
519 f5b4f2a3 Christos Stavrakakis
    client = vm.client
520 26563957 Giorgos Verigakis
    # Delete all firewall tags
521 efff6193 Giorgos Verigakis
    for t in _firewall_tags.values():
522 f5b4f2a3 Christos Stavrakakis
        client.DeleteInstanceTags(vm.backend_vm_id, [t], dry_run=settings.TEST)
523 37ca953f Christodoulos Psaltis
524 f5b4f2a3 Christos Stavrakakis
    client.AddInstanceTags(vm.backend_vm_id, [tag], dry_run=settings.TEST)
525 9afeb669 Kostas Papadimitriou
526 2da5f785 Giorgos Verigakis
    # XXX NOP ModifyInstance call to force process_net_status to run
527 2da5f785 Giorgos Verigakis
    # on the dispatcher
528 f5b4f2a3 Christos Stavrakakis
    vm.client.ModifyInstance(vm.backend_vm_id,
529 5eedb0e4 Vangelis Koukis
                        os_name=settings.GANETI_CREATEINSTANCE_KWARGS['os'])
530 5eedb0e4 Vangelis Koukis
531 41303ed0 Vangelis Koukis
532 f5b4f2a3 Christos Stavrakakis
def get_ganeti_instances(backend=None, bulk=False):
533 1a894bfe Christos Stavrakakis
    Instances = [c.client.GetInstances(bulk=bulk)\
534 1a894bfe Christos Stavrakakis
                 for c in get_backends(backend)]
535 f5b4f2a3 Christos Stavrakakis
    return reduce(list.__add__, Instances, [])
536 f5b4f2a3 Christos Stavrakakis
537 f5b4f2a3 Christos Stavrakakis
538 f5b4f2a3 Christos Stavrakakis
def get_ganeti_nodes(backend=None, bulk=False):
539 f5b4f2a3 Christos Stavrakakis
    Nodes = [c.client.GetNodes(bulk=bulk) for c in get_backends(backend)]
540 f5b4f2a3 Christos Stavrakakis
    return reduce(list.__add__, Nodes, [])
541 f5b4f2a3 Christos Stavrakakis
542 f5b4f2a3 Christos Stavrakakis
543 f5b4f2a3 Christos Stavrakakis
def get_ganeti_jobs(backend=None, bulk=False):
544 f5b4f2a3 Christos Stavrakakis
    Jobs = [c.client.GetJobs(bulk=bulk) for c in get_backends(backend)]
545 f5b4f2a3 Christos Stavrakakis
    return reduce(list.__add__, Jobs, [])
546 f5b4f2a3 Christos Stavrakakis
547 f5b4f2a3 Christos Stavrakakis
##
548 f5b4f2a3 Christos Stavrakakis
##
549 f5b4f2a3 Christos Stavrakakis
##
550 1a894bfe Christos Stavrakakis
551 1a894bfe Christos Stavrakakis
552 f5b4f2a3 Christos Stavrakakis
def get_backends(backend=None):
553 f5b4f2a3 Christos Stavrakakis
    if backend:
554 f5b4f2a3 Christos Stavrakakis
        return [backend]
555 cc7c0f44 Christos Stavrakakis
    return Backend.objects.filter(offline=False)
556 f5b4f2a3 Christos Stavrakakis
557 17852fe9 Giorgos Verigakis
558 1a894bfe Christos Stavrakakis
def get_physical_resources(backend):
559 1a894bfe Christos Stavrakakis
    """ Get the physical resources of a backend.
560 1a894bfe Christos Stavrakakis

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

563 1a894bfe Christos Stavrakakis
    """
564 1a894bfe Christos Stavrakakis
    nodes = get_ganeti_nodes(backend, bulk=True)
565 1a894bfe Christos Stavrakakis
    attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
566 1a894bfe Christos Stavrakakis
    res = {}
567 1a894bfe Christos Stavrakakis
    for a in attr:
568 1a894bfe Christos Stavrakakis
        res[a] = 0
569 1a894bfe Christos Stavrakakis
    for n in nodes:
570 1a894bfe Christos Stavrakakis
        # Filter out drained, offline and not vm_capable nodes since they will
571 1a894bfe Christos Stavrakakis
        # not take part in the vm allocation process
572 1a894bfe Christos Stavrakakis
        if n['vm_capable'] and not n['drained'] and not n['offline']\
573 1a894bfe Christos Stavrakakis
           and n['cnodes']:
574 1a894bfe Christos Stavrakakis
            for a in attr:
575 1a894bfe Christos Stavrakakis
                res[a] += int(n[a])
576 1a894bfe Christos Stavrakakis
    return res
577 1a894bfe Christos Stavrakakis
578 1a894bfe Christos Stavrakakis
579 1a894bfe Christos Stavrakakis
def update_resources(backend, resources=None):
580 1a894bfe Christos Stavrakakis
    """ Update the state of the backend resources in db.
581 1a894bfe Christos Stavrakakis

582 1a894bfe Christos Stavrakakis
    """
583 17852fe9 Giorgos Verigakis
584 1a894bfe Christos Stavrakakis
    if not resources:
585 1a894bfe Christos Stavrakakis
        resources = get_physical_resources(backend)
586 41303ed0 Vangelis Koukis
587 1a894bfe Christos Stavrakakis
    backend.mfree = resources['mfree']
588 1a894bfe Christos Stavrakakis
    backend.mtotal = resources['mtotal']
589 1a894bfe Christos Stavrakakis
    backend.dfree = resources['dfree']
590 1a894bfe Christos Stavrakakis
    backend.dtotal = resources['dtotal']
591 1a894bfe Christos Stavrakakis
    backend.pinst_cnt = resources['pinst_cnt']
592 1a894bfe Christos Stavrakakis
    backend.ctotal = resources['ctotal']
593 1a894bfe Christos Stavrakakis
    backend.updated = datetime.now()
594 1a894bfe Christos Stavrakakis
    backend.save()
595 1a894bfe Christos Stavrakakis
596 1a894bfe Christos Stavrakakis
597 1a894bfe Christos Stavrakakis
def get_memory_from_instances(backend):
598 1a894bfe Christos Stavrakakis
    """ Get the memory that is used from instances.
599 1a894bfe Christos Stavrakakis

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

603 1a894bfe Christos Stavrakakis
    """
604 1a894bfe Christos Stavrakakis
    instances = backend.client.GetInstances(bulk=True)
605 1a894bfe Christos Stavrakakis
    mem = 0
606 1a894bfe Christos Stavrakakis
    for i in instances:
607 1a894bfe Christos Stavrakakis
        mem += i['oper_ram']
608 1a894bfe Christos Stavrakakis
    return mem
609 b3d28af2 Christos Stavrakakis
610 b3d28af2 Christos Stavrakakis
##
611 b3d28af2 Christos Stavrakakis
## Synchronized operations for reconciliation
612 b3d28af2 Christos Stavrakakis
##
613 b3d28af2 Christos Stavrakakis
614 b3d28af2 Christos Stavrakakis
615 b3d28af2 Christos Stavrakakis
def create_network_synced(network, backend):
616 b3d28af2 Christos Stavrakakis
    result = _create_network_synced(network, backend)
617 b3d28af2 Christos Stavrakakis
    if result[0] != 'success':
618 b3d28af2 Christos Stavrakakis
        return result
619 b3d28af2 Christos Stavrakakis
    result = connect_network_synced(network, backend)
620 b3d28af2 Christos Stavrakakis
    return result
621 b3d28af2 Christos Stavrakakis
622 b3d28af2 Christos Stavrakakis
623 b3d28af2 Christos Stavrakakis
def _create_network_synced(network, backend):
624 b3d28af2 Christos Stavrakakis
    client = backend.client
625 b3d28af2 Christos Stavrakakis
    job = client.CreateNetwork(network.backend_id, network.subnet)
626 b3d28af2 Christos Stavrakakis
    return wait_for_job(client, job)
627 b3d28af2 Christos Stavrakakis
628 b3d28af2 Christos Stavrakakis
629 b3d28af2 Christos Stavrakakis
def connect_network_synced(network, backend):
630 b3d28af2 Christos Stavrakakis
    mode = network.public and 'routed' or 'bridged'
631 b3d28af2 Christos Stavrakakis
    client = backend.client
632 b3d28af2 Christos Stavrakakis
633 b3d28af2 Christos Stavrakakis
    for group in client.GetGroups():
634 b3d28af2 Christos Stavrakakis
        job = client.ConnectNetwork(network.backend_id, group, mode,
635 b3d28af2 Christos Stavrakakis
                                    network.link)
636 b3d28af2 Christos Stavrakakis
        result = wait_for_job(client, job)
637 b3d28af2 Christos Stavrakakis
        if result[0] != 'success':
638 b3d28af2 Christos Stavrakakis
            return result
639 b3d28af2 Christos Stavrakakis
640 b3d28af2 Christos Stavrakakis
    return result
641 b3d28af2 Christos Stavrakakis
642 b3d28af2 Christos Stavrakakis
643 b3d28af2 Christos Stavrakakis
def wait_for_job(client, jobid):
644 b3d28af2 Christos Stavrakakis
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
645 b3d28af2 Christos Stavrakakis
    status = result['job_info'][0]
646 b3d28af2 Christos Stavrakakis
    while status not in ['success', 'error', 'cancel']:
647 b3d28af2 Christos Stavrakakis
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
648 b3d28af2 Christos Stavrakakis
                                        [result], None)
649 b3d28af2 Christos Stavrakakis
        status = result['job_info'][0]
650 b3d28af2 Christos Stavrakakis
651 b3d28af2 Christos Stavrakakis
    if status == 'success':
652 b3d28af2 Christos Stavrakakis
        return (status, None)
653 b3d28af2 Christos Stavrakakis
    else:
654 b3d28af2 Christos Stavrakakis
        error = result['job_info'][1]
655 b3d28af2 Christos Stavrakakis
        return (status, error)