Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / servers.py @ 0c09b1c0

History | View | Annotate | Download (18.1 kB)

1 41a7fae7 Christos Stavrakakis
import logging
2 41a7fae7 Christos Stavrakakis
3 41a7fae7 Christos Stavrakakis
from socket import getfqdn
4 41a7fae7 Christos Stavrakakis
from functools import wraps
5 41a7fae7 Christos Stavrakakis
from django import dispatch
6 41a7fae7 Christos Stavrakakis
from django.db import transaction
7 41a7fae7 Christos Stavrakakis
from django.utils import simplejson as json
8 41a7fae7 Christos Stavrakakis
9 41a7fae7 Christos Stavrakakis
from snf_django.lib.api import faults
10 0c09b1c0 Christos Stavrakakis
from django.conf import settings
11 41a7fae7 Christos Stavrakakis
from synnefo import quotas
12 41a7fae7 Christos Stavrakakis
from synnefo.api import util
13 41a7fae7 Christos Stavrakakis
from synnefo.logic import backend
14 41a7fae7 Christos Stavrakakis
from synnefo.logic.backend_allocator import BackendAllocator
15 41a7fae7 Christos Stavrakakis
from synnefo.logic.rapi import GanetiApiError
16 cb66110b Christos Stavrakakis
from synnefo.db.models import (NetworkInterface, VirtualMachine, Network,
17 9ba6bb95 Christos Stavrakakis
                               VirtualMachineMetadata, FloatingIP)
18 41a7fae7 Christos Stavrakakis
19 41a7fae7 Christos Stavrakakis
from vncauthproxy.client import request_forwarding as request_vnc_forwarding
20 41a7fae7 Christos Stavrakakis
21 41a7fae7 Christos Stavrakakis
log = logging.getLogger(__name__)
22 41a7fae7 Christos Stavrakakis
23 41a7fae7 Christos Stavrakakis
# server creation signal
24 41a7fae7 Christos Stavrakakis
server_created = dispatch.Signal(providing_args=["created_vm_params"])
25 41a7fae7 Christos Stavrakakis
26 41a7fae7 Christos Stavrakakis
27 41a7fae7 Christos Stavrakakis
def validate_server_action(vm, action):
28 41a7fae7 Christos Stavrakakis
    if vm.deleted:
29 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Server '%s' has been deleted." % vm.id)
30 41a7fae7 Christos Stavrakakis
31 41a7fae7 Christos Stavrakakis
    # Destroyin a server should always be permitted
32 41a7fae7 Christos Stavrakakis
    if action == "DESTROY":
33 41a7fae7 Christos Stavrakakis
        return
34 41a7fae7 Christos Stavrakakis
35 41a7fae7 Christos Stavrakakis
    # Check that there is no pending action
36 41a7fae7 Christos Stavrakakis
    pending_action = vm.task
37 41a7fae7 Christos Stavrakakis
    if pending_action:
38 41a7fae7 Christos Stavrakakis
        if pending_action == "BUILD":
39 41a7fae7 Christos Stavrakakis
            raise faults.BuildInProgress("Server '%s' is being build." % vm.id)
40 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Can not perform '%s' action while there is a"
41 41a7fae7 Christos Stavrakakis
                                " pending '%s'." % (action, pending_action))
42 41a7fae7 Christos Stavrakakis
43 41a7fae7 Christos Stavrakakis
    # Check if action can be performed to VM's operstate
44 41a7fae7 Christos Stavrakakis
    operstate = vm.operstate
45 41a7fae7 Christos Stavrakakis
    if operstate == "BUILD":
46 41a7fae7 Christos Stavrakakis
        raise faults.BuildInProgress("Server '%s' is being build." % vm.id)
47 41a7fae7 Christos Stavrakakis
    elif (action == "START" and operstate == "STARTED") or\
48 41a7fae7 Christos Stavrakakis
         (action == "STOP" and operstate == "STOPPED") or\
49 41a7fae7 Christos Stavrakakis
         (action == "RESIZE" and operstate == "STARTED"):
50 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Can not perform '%s' action while server is"
51 41a7fae7 Christos Stavrakakis
                                " in '%s' state." % (action, operstate))
52 41a7fae7 Christos Stavrakakis
    return
53 41a7fae7 Christos Stavrakakis
54 41a7fae7 Christos Stavrakakis
55 41a7fae7 Christos Stavrakakis
def server_command(action):
56 41a7fae7 Christos Stavrakakis
    """Handle execution of a server action.
57 41a7fae7 Christos Stavrakakis

58 41a7fae7 Christos Stavrakakis
    Helper function to validate and execute a server action, handle quota
59 41a7fae7 Christos Stavrakakis
    commission and update the 'task' of the VM in the DB.
60 41a7fae7 Christos Stavrakakis

61 41a7fae7 Christos Stavrakakis
    1) Check if action can be performed. If it can, then there must be no
62 41a7fae7 Christos Stavrakakis
       pending task (with the exception of DESTROY).
63 41a7fae7 Christos Stavrakakis
    2) Handle previous commission if unresolved:
64 41a7fae7 Christos Stavrakakis
       * If it is not pending and it to accept, then accept
65 41a7fae7 Christos Stavrakakis
       * If it is not pending and to reject or is pending then reject it. Since
66 41a7fae7 Christos Stavrakakis
       the action can be performed only if there is no pending task, then there
67 41a7fae7 Christos Stavrakakis
       can be no pending commission. The exception is DESTROY, but in this case
68 41a7fae7 Christos Stavrakakis
       the commission can safely be rejected, and the dispatcher will generate
69 41a7fae7 Christos Stavrakakis
       the correct ones!
70 41a7fae7 Christos Stavrakakis
    3) Issue new commission and associate it with the VM. Also clear the task.
71 41a7fae7 Christos Stavrakakis
    4) Send job to ganeti
72 41a7fae7 Christos Stavrakakis
    5) Update task and commit
73 41a7fae7 Christos Stavrakakis
    """
74 41a7fae7 Christos Stavrakakis
    def decorator(func):
75 41a7fae7 Christos Stavrakakis
        @wraps(func)
76 41a7fae7 Christos Stavrakakis
        @transaction.commit_on_success
77 41a7fae7 Christos Stavrakakis
        def wrapper(vm, *args, **kwargs):
78 41a7fae7 Christos Stavrakakis
            user_id = vm.userid
79 41a7fae7 Christos Stavrakakis
            validate_server_action(vm, action)
80 41a7fae7 Christos Stavrakakis
81 41a7fae7 Christos Stavrakakis
            # Resolve(reject) previous serial if it is still pending!!
82 41a7fae7 Christos Stavrakakis
            previous_serial = vm.serial
83 41a7fae7 Christos Stavrakakis
            if previous_serial and not previous_serial.resolved:
84 41a7fae7 Christos Stavrakakis
                quotas.resolve_vm_commission(serial=previous_serial)
85 41a7fae7 Christos Stavrakakis
86 41a7fae7 Christos Stavrakakis
            # Check if action is quotable and issue the corresponding
87 41a7fae7 Christos Stavrakakis
            # commission
88 41a7fae7 Christos Stavrakakis
            serial = None
89 41a7fae7 Christos Stavrakakis
            commission_info = quotas.get_commission_info(vm, action=action)
90 41a7fae7 Christos Stavrakakis
            if commission_info is not None:
91 41a7fae7 Christos Stavrakakis
                # Issue new commission, associate it with the VM
92 9122ffab Christos Stavrakakis
                commission_name = "client: api, resource %s" % vm
93 41a7fae7 Christos Stavrakakis
                serial = quotas.issue_commission(user=user_id,
94 41a7fae7 Christos Stavrakakis
                                                 source=quotas.DEFAULT_SOURCE,
95 41a7fae7 Christos Stavrakakis
                                                 provisions=commission_info,
96 9122ffab Christos Stavrakakis
                                                 name=commission_name,
97 41a7fae7 Christos Stavrakakis
                                                 force=False,
98 41a7fae7 Christos Stavrakakis
                                                 auto_accept=False)
99 41a7fae7 Christos Stavrakakis
            vm.serial = serial
100 41a7fae7 Christos Stavrakakis
101 41a7fae7 Christos Stavrakakis
            # Send the job to Ganeti and get the associated jobID
102 41a7fae7 Christos Stavrakakis
            try:
103 41a7fae7 Christos Stavrakakis
                job_id = func(vm, *args, **kwargs)
104 41a7fae7 Christos Stavrakakis
            except Exception as e:
105 41a7fae7 Christos Stavrakakis
                if vm.serial is not None:
106 41a7fae7 Christos Stavrakakis
                    # Since the job never reached Ganeti, reject the commission
107 41a7fae7 Christos Stavrakakis
                    log.debug("Rejecting commission: '%s', could not perform"
108 41a7fae7 Christos Stavrakakis
                              " action '%s': %s" % (vm.serial,  action, e))
109 41a7fae7 Christos Stavrakakis
                    transaction.rollback()
110 41a7fae7 Christos Stavrakakis
                    quotas.reject_serial(vm.serial)
111 41a7fae7 Christos Stavrakakis
                    transaction.commit()
112 41a7fae7 Christos Stavrakakis
                raise
113 41a7fae7 Christos Stavrakakis
114 41a7fae7 Christos Stavrakakis
            log.info("user: %s, vm: %s, action: %s, job_id: %s, serial: %s",
115 41a7fae7 Christos Stavrakakis
                     user_id, vm.id, action, job_id, vm.serial)
116 41a7fae7 Christos Stavrakakis
117 41a7fae7 Christos Stavrakakis
            # store the new task in the VM
118 41a7fae7 Christos Stavrakakis
            if job_id is not None:
119 41a7fae7 Christos Stavrakakis
                vm.task = action
120 41a7fae7 Christos Stavrakakis
                vm.task_job_id = job_id
121 41a7fae7 Christos Stavrakakis
            vm.save()
122 41a7fae7 Christos Stavrakakis
123 41a7fae7 Christos Stavrakakis
            return vm
124 41a7fae7 Christos Stavrakakis
        return wrapper
125 41a7fae7 Christos Stavrakakis
    return decorator
126 41a7fae7 Christos Stavrakakis
127 41a7fae7 Christos Stavrakakis
128 41a7fae7 Christos Stavrakakis
@transaction.commit_manually
129 41a7fae7 Christos Stavrakakis
def create(userid, name, password, flavor, image, metadata={},
130 5aeb4e93 Christos Stavrakakis
           personality=[], private_networks=None, floating_ips=None,
131 5aeb4e93 Christos Stavrakakis
           use_backend=None):
132 41a7fae7 Christos Stavrakakis
    if use_backend is None:
133 41a7fae7 Christos Stavrakakis
        # Allocate backend to host the server. Commit after allocation to
134 41a7fae7 Christos Stavrakakis
        # release the locks hold by the backend allocator.
135 41a7fae7 Christos Stavrakakis
        try:
136 41a7fae7 Christos Stavrakakis
            backend_allocator = BackendAllocator()
137 41a7fae7 Christos Stavrakakis
            use_backend = backend_allocator.allocate(userid, flavor)
138 41a7fae7 Christos Stavrakakis
            if use_backend is None:
139 41a7fae7 Christos Stavrakakis
                log.error("No available backend for VM with flavor %s", flavor)
140 41a7fae7 Christos Stavrakakis
                raise faults.ServiceUnavailable("No available backends")
141 41a7fae7 Christos Stavrakakis
        except:
142 41a7fae7 Christos Stavrakakis
            transaction.rollback()
143 41a7fae7 Christos Stavrakakis
            raise
144 41a7fae7 Christos Stavrakakis
        else:
145 41a7fae7 Christos Stavrakakis
            transaction.commit()
146 41a7fae7 Christos Stavrakakis
147 6193628f Christos Stavrakakis
    if private_networks is None:
148 6193628f Christos Stavrakakis
        private_networks = []
149 6193628f Christos Stavrakakis
    if floating_ips is None:
150 6193628f Christos Stavrakakis
        floating_ips = []
151 6193628f Christos Stavrakakis
152 41a7fae7 Christos Stavrakakis
    # Fix flavor for archipelago
153 41a7fae7 Christos Stavrakakis
    disk_template, provider = util.get_flavor_provider(flavor)
154 41a7fae7 Christos Stavrakakis
    if provider:
155 41a7fae7 Christos Stavrakakis
        flavor.disk_template = disk_template
156 41a7fae7 Christos Stavrakakis
        flavor.disk_provider = provider
157 41a7fae7 Christos Stavrakakis
        flavor.disk_origin = None
158 41a7fae7 Christos Stavrakakis
        if provider == 'vlmc':
159 41a7fae7 Christos Stavrakakis
            flavor.disk_origin = image['checksum']
160 41a7fae7 Christos Stavrakakis
            image['backend_id'] = 'null'
161 41a7fae7 Christos Stavrakakis
    else:
162 41a7fae7 Christos Stavrakakis
        flavor.disk_provider = None
163 41a7fae7 Christos Stavrakakis
164 41a7fae7 Christos Stavrakakis
    try:
165 41a7fae7 Christos Stavrakakis
        # We must save the VM instance now, so that it gets a valid
166 41a7fae7 Christos Stavrakakis
        # vm.backend_vm_id.
167 41a7fae7 Christos Stavrakakis
        vm = VirtualMachine.objects.create(
168 41a7fae7 Christos Stavrakakis
            name=name,
169 41a7fae7 Christos Stavrakakis
            backend=use_backend,
170 41a7fae7 Christos Stavrakakis
            userid=userid,
171 41a7fae7 Christos Stavrakakis
            imageid=image["id"],
172 41a7fae7 Christos Stavrakakis
            flavor=flavor,
173 41a7fae7 Christos Stavrakakis
            action="CREATE")
174 41a7fae7 Christos Stavrakakis
175 41a7fae7 Christos Stavrakakis
        log.info("Created entry in DB for VM '%s'", vm)
176 41a7fae7 Christos Stavrakakis
177 41a7fae7 Christos Stavrakakis
        # dispatch server created signal
178 41a7fae7 Christos Stavrakakis
        server_created.send(sender=vm, created_vm_params={
179 41a7fae7 Christos Stavrakakis
            'img_id': image['backend_id'],
180 41a7fae7 Christos Stavrakakis
            'img_passwd': password,
181 41a7fae7 Christos Stavrakakis
            'img_format': str(image['format']),
182 41a7fae7 Christos Stavrakakis
            'img_personality': json.dumps(personality),
183 41a7fae7 Christos Stavrakakis
            'img_properties': json.dumps(image['metadata']),
184 41a7fae7 Christos Stavrakakis
        })
185 41a7fae7 Christos Stavrakakis
186 816d7588 Christos Stavrakakis
        nics = create_instance_nics(vm, userid, private_networks, floating_ips)
187 cb66110b Christos Stavrakakis
188 41a7fae7 Christos Stavrakakis
        # Also we must create the VM metadata in the same transaction.
189 41a7fae7 Christos Stavrakakis
        for key, val in metadata.items():
190 41a7fae7 Christos Stavrakakis
            VirtualMachineMetadata.objects.create(
191 41a7fae7 Christos Stavrakakis
                meta_key=key,
192 41a7fae7 Christos Stavrakakis
                meta_value=val,
193 41a7fae7 Christos Stavrakakis
                vm=vm)
194 41a7fae7 Christos Stavrakakis
        # Issue commission to Quotaholder and accept it since at the end of
195 41a7fae7 Christos Stavrakakis
        # this transaction the VirtualMachine object will be created in the DB.
196 41a7fae7 Christos Stavrakakis
        # Note: the following call does a commit!
197 41a7fae7 Christos Stavrakakis
        quotas.issue_and_accept_commission(vm)
198 41a7fae7 Christos Stavrakakis
    except:
199 41a7fae7 Christos Stavrakakis
        transaction.rollback()
200 41a7fae7 Christos Stavrakakis
        raise
201 41a7fae7 Christos Stavrakakis
    else:
202 41a7fae7 Christos Stavrakakis
        transaction.commit()
203 41a7fae7 Christos Stavrakakis
204 41a7fae7 Christos Stavrakakis
    try:
205 cb66110b Christos Stavrakakis
        jobID = backend.create_instance(vm, nics, flavor, image)
206 41a7fae7 Christos Stavrakakis
        # At this point the job is enqueued in the Ganeti backend
207 41a7fae7 Christos Stavrakakis
        vm.backendjobid = jobID
208 41a7fae7 Christos Stavrakakis
        vm.task = "BUILD"
209 41a7fae7 Christos Stavrakakis
        vm.task_job_id = jobID
210 41a7fae7 Christos Stavrakakis
        vm.save()
211 41a7fae7 Christos Stavrakakis
        transaction.commit()
212 f8675683 Christos Stavrakakis
        log.info("User %s created VM %s, NICs %s, Backend %s, JobID %s",
213 cb66110b Christos Stavrakakis
                 userid, vm, nics, backend, str(jobID))
214 41a7fae7 Christos Stavrakakis
    except GanetiApiError as e:
215 41a7fae7 Christos Stavrakakis
        log.exception("Can not communicate to backend %s: %s.",
216 41a7fae7 Christos Stavrakakis
                      backend, e)
217 41a7fae7 Christos Stavrakakis
        # Failed while enqueuing OP_INSTANCE_CREATE to backend. Restore
218 41a7fae7 Christos Stavrakakis
        # already reserved quotas by issuing a negative commission
219 41a7fae7 Christos Stavrakakis
        vm.operstate = "ERROR"
220 41a7fae7 Christos Stavrakakis
        vm.backendlogmsg = "Can not communicate to backend."
221 41a7fae7 Christos Stavrakakis
        vm.deleted = True
222 41a7fae7 Christos Stavrakakis
        vm.save()
223 41a7fae7 Christos Stavrakakis
        quotas.issue_and_accept_commission(vm, delete=True)
224 41a7fae7 Christos Stavrakakis
        raise
225 41a7fae7 Christos Stavrakakis
    except:
226 41a7fae7 Christos Stavrakakis
        transaction.rollback()
227 41a7fae7 Christos Stavrakakis
        raise
228 41a7fae7 Christos Stavrakakis
229 41a7fae7 Christos Stavrakakis
    return vm
230 41a7fae7 Christos Stavrakakis
231 41a7fae7 Christos Stavrakakis
232 816d7588 Christos Stavrakakis
def create_instance_nics(vm, userid, private_networks=[], floating_ips=[]):
233 cb66110b Christos Stavrakakis
    """Create NICs for VirtualMachine.
234 cb66110b Christos Stavrakakis

235 cb66110b Christos Stavrakakis
    Helper function for allocating IP addresses and creating NICs in the DB
236 cb66110b Christos Stavrakakis
    for a VirtualMachine. Created NICs are the combination of the default
237 cb66110b Christos Stavrakakis
    network policy (defined by administration settings) and the private
238 cb66110b Christos Stavrakakis
    networks defined by the user.
239 cb66110b Christos Stavrakakis

240 cb66110b Christos Stavrakakis
    """
241 cb66110b Christos Stavrakakis
    attachments = []
242 cb66110b Christos Stavrakakis
    for network_id in settings.DEFAULT_INSTANCE_NETWORKS:
243 cb66110b Christos Stavrakakis
        network, address = None, None
244 9446e7e5 Christos Stavrakakis
        if network_id == "SNF:ANY_PUBLIC":
245 660b9f3b Christos Stavrakakis
            network, address = util.allocate_public_address(backend=vm.backend)
246 cb66110b Christos Stavrakakis
        else:
247 cb66110b Christos Stavrakakis
            try:
248 cb66110b Christos Stavrakakis
                network = Network.objects.get(id=network_id, deleted=False)
249 cb66110b Christos Stavrakakis
            except Network.DoesNotExist:
250 cb66110b Christos Stavrakakis
                msg = "Invalid configuration. Setting"\
251 cb66110b Christos Stavrakakis
                      " 'DEFAULT_INSTANCE_NETWORKS' contains invalid"\
252 cb66110b Christos Stavrakakis
                      " network '%s'" % network_id
253 cb66110b Christos Stavrakakis
                log.error(msg)
254 cb66110b Christos Stavrakakis
                raise Exception(msg)
255 5aeb4e93 Christos Stavrakakis
            if network.subnet is not None and network.dhcp:
256 cb66110b Christos Stavrakakis
                address = util.get_network_free_address(network)
257 cb66110b Christos Stavrakakis
        attachments.append((network, address))
258 816d7588 Christos Stavrakakis
    for address in floating_ips:
259 f8675683 Christos Stavrakakis
        floating_ip = add_floating_ip_to_vm(vm=vm, address=address)
260 816d7588 Christos Stavrakakis
        network = floating_ip.network
261 816d7588 Christos Stavrakakis
        attachments.append((network, address))
262 cb66110b Christos Stavrakakis
    for network_id in private_networks:
263 cb66110b Christos Stavrakakis
        network, address = None, None
264 cb66110b Christos Stavrakakis
        network = util.get_network(network_id, userid, non_deleted=True)
265 cb66110b Christos Stavrakakis
        if network.public:
266 cb66110b Christos Stavrakakis
            raise faults.Forbidden("Can not connect to public network")
267 cb66110b Christos Stavrakakis
        if network.dhcp:
268 cb66110b Christos Stavrakakis
            address = util.get_network_free_address(network)
269 cb66110b Christos Stavrakakis
        attachments.append((network, address))
270 cb66110b Christos Stavrakakis
271 cb66110b Christos Stavrakakis
    nics = []
272 cb66110b Christos Stavrakakis
    for index, (network, address) in enumerate(attachments):
273 cb66110b Christos Stavrakakis
        # Create VM's public NIC. Do not wait notification form ganeti
274 cb66110b Christos Stavrakakis
        # hooks to create this NIC, because if the hooks never run (e.g.
275 cb66110b Christos Stavrakakis
        # building error) the VM's public IP address will never be
276 cb66110b Christos Stavrakakis
        # released!
277 cb66110b Christos Stavrakakis
        nic = NetworkInterface.objects.create(machine=vm, network=network,
278 cb66110b Christos Stavrakakis
                                              index=index, ipv4=address,
279 cb66110b Christos Stavrakakis
                                              state="BUILDING")
280 cb66110b Christos Stavrakakis
        nics.append(nic)
281 cb66110b Christos Stavrakakis
    return nics
282 cb66110b Christos Stavrakakis
283 cb66110b Christos Stavrakakis
284 41a7fae7 Christos Stavrakakis
@server_command("DESTROY")
285 41a7fae7 Christos Stavrakakis
def destroy(vm):
286 41a7fae7 Christos Stavrakakis
    log.info("Deleting VM %s", vm)
287 41a7fae7 Christos Stavrakakis
    return backend.delete_instance(vm)
288 41a7fae7 Christos Stavrakakis
289 41a7fae7 Christos Stavrakakis
290 41a7fae7 Christos Stavrakakis
@server_command("START")
291 41a7fae7 Christos Stavrakakis
def start(vm):
292 41a7fae7 Christos Stavrakakis
    log.info("Starting VM %s", vm)
293 41a7fae7 Christos Stavrakakis
    return backend.startup_instance(vm)
294 41a7fae7 Christos Stavrakakis
295 41a7fae7 Christos Stavrakakis
296 41a7fae7 Christos Stavrakakis
@server_command("STOP")
297 41a7fae7 Christos Stavrakakis
def stop(vm):
298 41a7fae7 Christos Stavrakakis
    log.info("Stopping VM %s", vm)
299 41a7fae7 Christos Stavrakakis
    return backend.shutdown_instance(vm)
300 41a7fae7 Christos Stavrakakis
301 41a7fae7 Christos Stavrakakis
302 41a7fae7 Christos Stavrakakis
@server_command("REBOOT")
303 41a7fae7 Christos Stavrakakis
def reboot(vm, reboot_type):
304 41a7fae7 Christos Stavrakakis
    if reboot_type not in ("SOFT", "HARD"):
305 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Malformed request. Invalid reboot"
306 41a7fae7 Christos Stavrakakis
                                " type %s" % reboot_type)
307 41a7fae7 Christos Stavrakakis
    log.info("Rebooting VM %s. Type %s", vm, reboot_type)
308 41a7fae7 Christos Stavrakakis
309 41a7fae7 Christos Stavrakakis
    return backend.reboot_instance(vm, reboot_type.lower())
310 41a7fae7 Christos Stavrakakis
311 41a7fae7 Christos Stavrakakis
312 41a7fae7 Christos Stavrakakis
@server_command("RESIZE")
313 41a7fae7 Christos Stavrakakis
def resize(vm, flavor):
314 41a7fae7 Christos Stavrakakis
    old_flavor = vm.flavor
315 41a7fae7 Christos Stavrakakis
    # User requested the same flavor
316 41a7fae7 Christos Stavrakakis
    if old_flavor.id == flavor.id:
317 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Server '%s' flavor is already '%s'."
318 41a7fae7 Christos Stavrakakis
                                % (vm, flavor))
319 41a7fae7 Christos Stavrakakis
        return None
320 41a7fae7 Christos Stavrakakis
    # Check that resize can be performed
321 41a7fae7 Christos Stavrakakis
    if old_flavor.disk != flavor.disk:
322 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Can not resize instance disk.")
323 41a7fae7 Christos Stavrakakis
    if old_flavor.disk_template != flavor.disk_template:
324 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Can not change instance disk template.")
325 41a7fae7 Christos Stavrakakis
326 41a7fae7 Christos Stavrakakis
    log.info("Resizing VM from flavor '%s' to '%s", old_flavor, flavor)
327 41a7fae7 Christos Stavrakakis
    commission_info = {"cyclades.cpu": flavor.cpu - old_flavor.cpu,
328 f05a25af Christos Stavrakakis
                       "cyclades.ram": 1048576 * (flavor.ram - old_flavor.ram)}
329 41a7fae7 Christos Stavrakakis
    # Save serial to VM, since it is needed by server_command decorator
330 41a7fae7 Christos Stavrakakis
    vm.serial = quotas.issue_commission(user=vm.userid,
331 41a7fae7 Christos Stavrakakis
                                        source=quotas.DEFAULT_SOURCE,
332 9122ffab Christos Stavrakakis
                                        provisions=commission_info,
333 9122ffab Christos Stavrakakis
                                        name="resource: %s. resize" % vm)
334 41a7fae7 Christos Stavrakakis
    return backend.resize_instance(vm, vcpus=flavor.cpu, memory=flavor.ram)
335 41a7fae7 Christos Stavrakakis
336 41a7fae7 Christos Stavrakakis
337 41a7fae7 Christos Stavrakakis
@server_command("SET_FIREWALL_PROFILE")
338 b2791a77 Christos Stavrakakis
def set_firewall_profile(vm, profile, index=0):
339 b2791a77 Christos Stavrakakis
    log.info("Setting VM %s, NIC index %s, firewall %s", vm, index, profile)
340 41a7fae7 Christos Stavrakakis
341 41a7fae7 Christos Stavrakakis
    if profile not in [x[0] for x in NetworkInterface.FIREWALL_PROFILES]:
342 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Unsupported firewall profile")
343 b2791a77 Christos Stavrakakis
    backend.set_firewall_profile(vm, profile=profile, index=index)
344 41a7fae7 Christos Stavrakakis
    return None
345 41a7fae7 Christos Stavrakakis
346 41a7fae7 Christos Stavrakakis
347 41a7fae7 Christos Stavrakakis
@server_command("CONNECT")
348 41a7fae7 Christos Stavrakakis
def connect(vm, network):
349 41a7fae7 Christos Stavrakakis
    if network.state != 'ACTIVE':
350 41a7fae7 Christos Stavrakakis
        raise faults.BuildInProgress('Network not active yet')
351 41a7fae7 Christos Stavrakakis
352 41a7fae7 Christos Stavrakakis
    address = None
353 5aeb4e93 Christos Stavrakakis
    if network.subnet is not None and network.dhcp:
354 41a7fae7 Christos Stavrakakis
        # Get a free IP from the address pool.
355 34c03a51 Christos Stavrakakis
        address = util.get_network_free_address(network)
356 41a7fae7 Christos Stavrakakis
357 41a7fae7 Christos Stavrakakis
    log.info("Connecting VM %s to Network %s(%s)", vm, network, address)
358 41a7fae7 Christos Stavrakakis
359 41a7fae7 Christos Stavrakakis
    return backend.connect_to_network(vm, network, address)
360 41a7fae7 Christos Stavrakakis
361 41a7fae7 Christos Stavrakakis
362 41a7fae7 Christos Stavrakakis
@server_command("DISCONNECT")
363 41a7fae7 Christos Stavrakakis
def disconnect(vm, nic_index):
364 41a7fae7 Christos Stavrakakis
    nic = util.get_nic_from_index(vm, nic_index)
365 41a7fae7 Christos Stavrakakis
366 41a7fae7 Christos Stavrakakis
    log.info("Removing NIC %s from VM %s", str(nic.index), vm)
367 41a7fae7 Christos Stavrakakis
368 41a7fae7 Christos Stavrakakis
    if nic.dirty:
369 41a7fae7 Christos Stavrakakis
        raise faults.BuildInProgress('Machine is busy.')
370 41a7fae7 Christos Stavrakakis
    else:
371 41a7fae7 Christos Stavrakakis
        vm.nics.all().update(dirty=True)
372 41a7fae7 Christos Stavrakakis
373 41a7fae7 Christos Stavrakakis
    return backend.disconnect_from_network(vm, nic)
374 41a7fae7 Christos Stavrakakis
375 41a7fae7 Christos Stavrakakis
376 41a7fae7 Christos Stavrakakis
def console(vm, console_type):
377 41a7fae7 Christos Stavrakakis
    """Arrange for an OOB console of the specified type
378 41a7fae7 Christos Stavrakakis

379 41a7fae7 Christos Stavrakakis
    This method arranges for an OOB console of the specified type.
380 41a7fae7 Christos Stavrakakis
    Only consoles of type "vnc" are supported for now.
381 41a7fae7 Christos Stavrakakis

382 41a7fae7 Christos Stavrakakis
    It uses a running instance of vncauthproxy to setup proper
383 41a7fae7 Christos Stavrakakis
    VNC forwarding with a random password, then returns the necessary
384 41a7fae7 Christos Stavrakakis
    VNC connection info to the caller.
385 41a7fae7 Christos Stavrakakis

386 41a7fae7 Christos Stavrakakis
    """
387 41a7fae7 Christos Stavrakakis
    log.info("Get console  VM %s, type %s", vm, console_type)
388 41a7fae7 Christos Stavrakakis
389 41a7fae7 Christos Stavrakakis
    # Use RAPI to get VNC console information for this instance
390 41a7fae7 Christos Stavrakakis
    if vm.operstate != "STARTED":
391 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest('Server not in ACTIVE state.')
392 41a7fae7 Christos Stavrakakis
393 41a7fae7 Christos Stavrakakis
    if settings.TEST:
394 41a7fae7 Christos Stavrakakis
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
395 41a7fae7 Christos Stavrakakis
    else:
396 41a7fae7 Christos Stavrakakis
        console_data = backend.get_instance_console(vm)
397 41a7fae7 Christos Stavrakakis
398 41a7fae7 Christos Stavrakakis
    if console_data['kind'] != 'vnc':
399 41a7fae7 Christos Stavrakakis
        message = 'got console of kind %s, not "vnc"' % console_data['kind']
400 41a7fae7 Christos Stavrakakis
        raise faults.ServiceUnavailable(message)
401 41a7fae7 Christos Stavrakakis
402 41a7fae7 Christos Stavrakakis
    # Let vncauthproxy decide on the source port.
403 41a7fae7 Christos Stavrakakis
    # The alternative: static allocation, e.g.
404 41a7fae7 Christos Stavrakakis
    # sport = console_data['port'] - 1000
405 41a7fae7 Christos Stavrakakis
    sport = 0
406 41a7fae7 Christos Stavrakakis
    daddr = console_data['host']
407 41a7fae7 Christos Stavrakakis
    dport = console_data['port']
408 41a7fae7 Christos Stavrakakis
    password = util.random_password()
409 41a7fae7 Christos Stavrakakis
410 41a7fae7 Christos Stavrakakis
    if settings.TEST:
411 41a7fae7 Christos Stavrakakis
        fwd = {'source_port': 1234, 'status': 'OK'}
412 41a7fae7 Christos Stavrakakis
    else:
413 41a7fae7 Christos Stavrakakis
        fwd = request_vnc_forwarding(sport, daddr, dport, password)
414 41a7fae7 Christos Stavrakakis
415 41a7fae7 Christos Stavrakakis
    if fwd['status'] != "OK":
416 41a7fae7 Christos Stavrakakis
        raise faults.ServiceUnavailable('vncauthproxy returned error status')
417 41a7fae7 Christos Stavrakakis
418 41a7fae7 Christos Stavrakakis
    # Verify that the VNC server settings haven't changed
419 41a7fae7 Christos Stavrakakis
    if not settings.TEST:
420 41a7fae7 Christos Stavrakakis
        if console_data != backend.get_instance_console(vm):
421 41a7fae7 Christos Stavrakakis
            raise faults.ServiceUnavailable('VNC Server settings changed.')
422 41a7fae7 Christos Stavrakakis
423 41a7fae7 Christos Stavrakakis
    console = {
424 41a7fae7 Christos Stavrakakis
        'type': 'vnc',
425 41a7fae7 Christos Stavrakakis
        'host': getfqdn(),
426 41a7fae7 Christos Stavrakakis
        'port': fwd['source_port'],
427 41a7fae7 Christos Stavrakakis
        'password': password}
428 41a7fae7 Christos Stavrakakis
429 41a7fae7 Christos Stavrakakis
    return console
430 9ba6bb95 Christos Stavrakakis
431 9ba6bb95 Christos Stavrakakis
432 f8675683 Christos Stavrakakis
@server_command("CONNECT")
433 9ba6bb95 Christos Stavrakakis
def add_floating_ip(vm, address):
434 816d7588 Christos Stavrakakis
    floating_ip = add_floating_ip_to_vm(vm, address)
435 816d7588 Christos Stavrakakis
    log.info("Connecting VM %s to floating IP %s", vm, floating_ip)
436 816d7588 Christos Stavrakakis
    return backend.connect_to_network(vm, floating_ip.network, address)
437 816d7588 Christos Stavrakakis
438 816d7588 Christos Stavrakakis
439 816d7588 Christos Stavrakakis
def add_floating_ip_to_vm(vm, address):
440 816d7588 Christos Stavrakakis
    """Get a floating IP by it's address and add it to VirtualMachine.
441 816d7588 Christos Stavrakakis

442 816d7588 Christos Stavrakakis
    Helper function for looking up a FloatingIP by it's address and associating
443 816d7588 Christos Stavrakakis
    it with a VirtualMachine object (without adding the NIC in the Ganeti
444 816d7588 Christos Stavrakakis
    backend!). This function also checks if the floating IP is currently used
445 816d7588 Christos Stavrakakis
    by any instance and if it is available in the Backend that hosts the VM.
446 816d7588 Christos Stavrakakis

447 816d7588 Christos Stavrakakis
    """
448 9ba6bb95 Christos Stavrakakis
    user_id = vm.userid
449 9ba6bb95 Christos Stavrakakis
    try:
450 816d7588 Christos Stavrakakis
        # Get lock in VM, to guarantee that floating IP will only by assigned
451 816d7588 Christos Stavrakakis
        # once
452 9ba6bb95 Christos Stavrakakis
        floating_ip = FloatingIP.objects.select_for_update()\
453 9ba6bb95 Christos Stavrakakis
                                        .get(userid=user_id, ipv4=address,
454 9ba6bb95 Christos Stavrakakis
                                             deleted=False)
455 9ba6bb95 Christos Stavrakakis
    except FloatingIP.DoesNotExist:
456 9ba6bb95 Christos Stavrakakis
        raise faults.ItemNotFound("Floating IP '%s' does not exist" % address)
457 9ba6bb95 Christos Stavrakakis
458 9ba6bb95 Christos Stavrakakis
    if floating_ip.in_use():
459 9ba6bb95 Christos Stavrakakis
        raise faults.Conflict("Floating IP '%s' already in use" %
460 9ba6bb95 Christos Stavrakakis
                              floating_ip.id)
461 9ba6bb95 Christos Stavrakakis
462 e7f74e7d Christos Stavrakakis
    bnet = floating_ip.network.backend_networks.filter(backend=vm.backend_id)
463 e7f74e7d Christos Stavrakakis
    if not bnet.exists():
464 e7f74e7d Christos Stavrakakis
        msg = "Network '%s' is a floating IP pool, but it not connected"\
465 e7f74e7d Christos Stavrakakis
              " to backend '%s'" % (floating_ip.network, vm.backend)
466 e7f74e7d Christos Stavrakakis
        raise faults.ServiceUnavailable(msg)
467 e7f74e7d Christos Stavrakakis
468 9ba6bb95 Christos Stavrakakis
    floating_ip.machine = vm
469 9ba6bb95 Christos Stavrakakis
    floating_ip.save()
470 816d7588 Christos Stavrakakis
    return floating_ip
471 9ba6bb95 Christos Stavrakakis
472 9ba6bb95 Christos Stavrakakis
473 f8675683 Christos Stavrakakis
@server_command("DISCONNECT")
474 9ba6bb95 Christos Stavrakakis
def remove_floating_ip(vm, address):
475 9ba6bb95 Christos Stavrakakis
    user_id = vm.userid
476 9ba6bb95 Christos Stavrakakis
    try:
477 9ba6bb95 Christos Stavrakakis
        floating_ip = FloatingIP.objects.select_for_update()\
478 9ba6bb95 Christos Stavrakakis
                                        .get(userid=user_id, ipv4=address,
479 9ba6bb95 Christos Stavrakakis
                                             deleted=False, machine=vm)
480 9ba6bb95 Christos Stavrakakis
    except FloatingIP.DoesNotExist:
481 9ba6bb95 Christos Stavrakakis
        raise faults.ItemNotFound("Floating IP '%s' does not exist" % address)
482 9ba6bb95 Christos Stavrakakis
483 9ba6bb95 Christos Stavrakakis
    try:
484 9ba6bb95 Christos Stavrakakis
        nic = NetworkInterface.objects.get(machine=vm, ipv4=address)
485 9ba6bb95 Christos Stavrakakis
    except NetworkInterface.DoesNotExist:
486 9ba6bb95 Christos Stavrakakis
        raise faults.ItemNotFound("Floating IP '%s' is not attached to"
487 9ba6bb95 Christos Stavrakakis
                                  "VM '%s'" % (floating_ip, vm))
488 9ba6bb95 Christos Stavrakakis
489 9ba6bb95 Christos Stavrakakis
    log.info("Removing NIC %s from VM %s. Floating IP '%s'", str(nic.index),
490 9ba6bb95 Christos Stavrakakis
             vm, floating_ip)
491 9ba6bb95 Christos Stavrakakis
492 9ba6bb95 Christos Stavrakakis
    return backend.disconnect_from_network(vm, nic)