Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / servers.py @ 5c8076b6

History | View | Annotate | Download (18.8 kB)

1 41a7fae7 Christos Stavrakakis
import logging
2 8e67ea28 Christos Stavrakakis
import datetime
3 41a7fae7 Christos Stavrakakis
4 41a7fae7 Christos Stavrakakis
from socket import getfqdn
5 41a7fae7 Christos Stavrakakis
from functools import wraps
6 41a7fae7 Christos Stavrakakis
from django import dispatch
7 41a7fae7 Christos Stavrakakis
from django.db import transaction
8 41a7fae7 Christos Stavrakakis
from django.utils import simplejson as json
9 41a7fae7 Christos Stavrakakis
10 41a7fae7 Christos Stavrakakis
from snf_django.lib.api import faults
11 0c09b1c0 Christos Stavrakakis
from django.conf import settings
12 41a7fae7 Christos Stavrakakis
from synnefo import quotas
13 41a7fae7 Christos Stavrakakis
from synnefo.api import util
14 41a7fae7 Christos Stavrakakis
from synnefo.logic import backend
15 41a7fae7 Christos Stavrakakis
from synnefo.logic.backend_allocator import BackendAllocator
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 562bf712 Christos Stavrakakis
    if operstate == "BUILD" and action != "BUILD":
46 41a7fae7 Christos Stavrakakis
        raise faults.BuildInProgress("Server '%s' is being build." % vm.id)
47 9599e997 Christos Stavrakakis
    elif (action == "START" and operstate != "STOPPED") or\
48 9599e997 Christos Stavrakakis
         (action == "STOP" and operstate != "STARTED") or\
49 9599e997 Christos Stavrakakis
         (action == "RESIZE" and operstate != "STOPPED") or\
50 9599e997 Christos Stavrakakis
         (action in ["CONNECT", "DISCONNECT"] and operstate != "STOPPED"
51 9599e997 Christos Stavrakakis
          and not settings.GANETI_USE_HOTPLUG):
52 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Can not perform '%s' action while server is"
53 41a7fae7 Christos Stavrakakis
                                " in '%s' state." % (action, operstate))
54 41a7fae7 Christos Stavrakakis
    return
55 41a7fae7 Christos Stavrakakis
56 41a7fae7 Christos Stavrakakis
57 41a7fae7 Christos Stavrakakis
def server_command(action):
58 41a7fae7 Christos Stavrakakis
    """Handle execution of a server action.
59 41a7fae7 Christos Stavrakakis

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

63 41a7fae7 Christos Stavrakakis
    1) Check if action can be performed. If it can, then there must be no
64 41a7fae7 Christos Stavrakakis
       pending task (with the exception of DESTROY).
65 41a7fae7 Christos Stavrakakis
    2) Handle previous commission if unresolved:
66 41a7fae7 Christos Stavrakakis
       * If it is not pending and it to accept, then accept
67 41a7fae7 Christos Stavrakakis
       * If it is not pending and to reject or is pending then reject it. Since
68 41a7fae7 Christos Stavrakakis
       the action can be performed only if there is no pending task, then there
69 41a7fae7 Christos Stavrakakis
       can be no pending commission. The exception is DESTROY, but in this case
70 41a7fae7 Christos Stavrakakis
       the commission can safely be rejected, and the dispatcher will generate
71 41a7fae7 Christos Stavrakakis
       the correct ones!
72 41a7fae7 Christos Stavrakakis
    3) Issue new commission and associate it with the VM. Also clear the task.
73 41a7fae7 Christos Stavrakakis
    4) Send job to ganeti
74 41a7fae7 Christos Stavrakakis
    5) Update task and commit
75 41a7fae7 Christos Stavrakakis
    """
76 41a7fae7 Christos Stavrakakis
    def decorator(func):
77 41a7fae7 Christos Stavrakakis
        @wraps(func)
78 41a7fae7 Christos Stavrakakis
        @transaction.commit_on_success
79 41a7fae7 Christos Stavrakakis
        def wrapper(vm, *args, **kwargs):
80 41a7fae7 Christos Stavrakakis
            user_id = vm.userid
81 41a7fae7 Christos Stavrakakis
            validate_server_action(vm, action)
82 c9fefba3 Christos Stavrakakis
            vm.action = action
83 41a7fae7 Christos Stavrakakis
84 5c8076b6 Christos Stavrakakis
            commission_name = "client: api, resource: %s" % vm
85 5c8076b6 Christos Stavrakakis
            quotas.handle_resource_commission(vm, action=action,
86 5c8076b6 Christos Stavrakakis
                                              commission_name=commission_name)
87 5c8076b6 Christos Stavrakakis
            vm.save()
88 41a7fae7 Christos Stavrakakis
89 562bf712 Christos Stavrakakis
            # XXX: Special case for server creation!
90 562bf712 Christos Stavrakakis
            if action == "BUILD":
91 562bf712 Christos Stavrakakis
                # Perform a commit, because the VirtualMachine must be saved to
92 562bf712 Christos Stavrakakis
                # DB before the OP_INSTANCE_CREATE job in enqueued in Ganeti.
93 562bf712 Christos Stavrakakis
                # Otherwise, messages will arrive from snf-dispatcher about
94 562bf712 Christos Stavrakakis
                # this instance, before the VM is stored in DB.
95 562bf712 Christos Stavrakakis
                transaction.commit()
96 562bf712 Christos Stavrakakis
                # After committing the locks are released. Refetch the instance
97 562bf712 Christos Stavrakakis
                # to guarantee x-lock.
98 562bf712 Christos Stavrakakis
                vm = VirtualMachine.objects.select_for_update().get(id=vm.id)
99 562bf712 Christos Stavrakakis
100 41a7fae7 Christos Stavrakakis
            # Send the job to Ganeti and get the associated jobID
101 41a7fae7 Christos Stavrakakis
            try:
102 41a7fae7 Christos Stavrakakis
                job_id = func(vm, *args, **kwargs)
103 41a7fae7 Christos Stavrakakis
            except Exception as e:
104 41a7fae7 Christos Stavrakakis
                if vm.serial is not None:
105 41a7fae7 Christos Stavrakakis
                    # Since the job never reached Ganeti, reject the commission
106 41a7fae7 Christos Stavrakakis
                    log.debug("Rejecting commission: '%s', could not perform"
107 41a7fae7 Christos Stavrakakis
                              " action '%s': %s" % (vm.serial,  action, e))
108 41a7fae7 Christos Stavrakakis
                    transaction.rollback()
109 41a7fae7 Christos Stavrakakis
                    quotas.reject_serial(vm.serial)
110 41a7fae7 Christos Stavrakakis
                    transaction.commit()
111 41a7fae7 Christos Stavrakakis
                raise
112 41a7fae7 Christos Stavrakakis
113 41a7fae7 Christos Stavrakakis
            log.info("user: %s, vm: %s, action: %s, job_id: %s, serial: %s",
114 41a7fae7 Christos Stavrakakis
                     user_id, vm.id, action, job_id, vm.serial)
115 41a7fae7 Christos Stavrakakis
116 41a7fae7 Christos Stavrakakis
            # store the new task in the VM
117 41a7fae7 Christos Stavrakakis
            if job_id is not None:
118 41a7fae7 Christos Stavrakakis
                vm.task = action
119 41a7fae7 Christos Stavrakakis
                vm.task_job_id = job_id
120 41a7fae7 Christos Stavrakakis
            vm.save()
121 41a7fae7 Christos Stavrakakis
122 41a7fae7 Christos Stavrakakis
            return vm
123 41a7fae7 Christos Stavrakakis
        return wrapper
124 41a7fae7 Christos Stavrakakis
    return decorator
125 41a7fae7 Christos Stavrakakis
126 41a7fae7 Christos Stavrakakis
127 562bf712 Christos Stavrakakis
@transaction.commit_on_success
128 41a7fae7 Christos Stavrakakis
def create(userid, name, password, flavor, image, metadata={},
129 5aeb4e93 Christos Stavrakakis
           personality=[], private_networks=None, floating_ips=None,
130 5aeb4e93 Christos Stavrakakis
           use_backend=None):
131 41a7fae7 Christos Stavrakakis
    if use_backend is None:
132 562bf712 Christos Stavrakakis
        # Allocate server to a Ganeti backend
133 562bf712 Christos Stavrakakis
        use_backend = allocate_new_server(userid, flavor)
134 41a7fae7 Christos Stavrakakis
135 6193628f Christos Stavrakakis
    if private_networks is None:
136 6193628f Christos Stavrakakis
        private_networks = []
137 6193628f Christos Stavrakakis
    if floating_ips is None:
138 6193628f Christos Stavrakakis
        floating_ips = []
139 6193628f Christos Stavrakakis
140 41a7fae7 Christos Stavrakakis
    # Fix flavor for archipelago
141 41a7fae7 Christos Stavrakakis
    disk_template, provider = util.get_flavor_provider(flavor)
142 41a7fae7 Christos Stavrakakis
    if provider:
143 41a7fae7 Christos Stavrakakis
        flavor.disk_template = disk_template
144 41a7fae7 Christos Stavrakakis
        flavor.disk_provider = provider
145 41a7fae7 Christos Stavrakakis
        flavor.disk_origin = None
146 41a7fae7 Christos Stavrakakis
        if provider == 'vlmc':
147 41a7fae7 Christos Stavrakakis
            flavor.disk_origin = image['checksum']
148 41a7fae7 Christos Stavrakakis
            image['backend_id'] = 'null'
149 41a7fae7 Christos Stavrakakis
    else:
150 41a7fae7 Christos Stavrakakis
        flavor.disk_provider = None
151 41a7fae7 Christos Stavrakakis
152 562bf712 Christos Stavrakakis
    # We must save the VM instance now, so that it gets a valid
153 562bf712 Christos Stavrakakis
    # vm.backend_vm_id.
154 562bf712 Christos Stavrakakis
    vm = VirtualMachine.objects.create(name=name,
155 562bf712 Christos Stavrakakis
                                       backend=use_backend,
156 562bf712 Christos Stavrakakis
                                       userid=userid,
157 562bf712 Christos Stavrakakis
                                       imageid=image["id"],
158 562bf712 Christos Stavrakakis
                                       flavor=flavor,
159 562bf712 Christos Stavrakakis
                                       operstate="BUILD")
160 562bf712 Christos Stavrakakis
    log.info("Created entry in DB for VM '%s'", vm)
161 562bf712 Christos Stavrakakis
162 562bf712 Christos Stavrakakis
    nics = create_instance_nics(vm, userid, private_networks, floating_ips)
163 562bf712 Christos Stavrakakis
164 562bf712 Christos Stavrakakis
    for key, val in metadata.items():
165 562bf712 Christos Stavrakakis
        VirtualMachineMetadata.objects.create(
166 562bf712 Christos Stavrakakis
            meta_key=key,
167 562bf712 Christos Stavrakakis
            meta_value=val,
168 562bf712 Christos Stavrakakis
            vm=vm)
169 41a7fae7 Christos Stavrakakis
170 41a7fae7 Christos Stavrakakis
    try:
171 562bf712 Christos Stavrakakis
        # Create the server in Ganeti.
172 562bf712 Christos Stavrakakis
        create_server(vm, nics, flavor, image, personality, password)
173 41a7fae7 Christos Stavrakakis
    except:
174 8e67ea28 Christos Stavrakakis
        # If an exception is raised, then the user will never get the VM id.
175 8e67ea28 Christos Stavrakakis
        # In order to delete it from DB and release it's resources, we
176 8e67ea28 Christos Stavrakakis
        # mock a successful OP_INSTANCE_REMOVE job.
177 8e67ea28 Christos Stavrakakis
        backend.process_op_status(vm=vm, etime=datetime.datetime.now(),
178 8e67ea28 Christos Stavrakakis
                                  jobid=-0,
179 8e67ea28 Christos Stavrakakis
                                  opcode="OP_INSTANCE_REMOVE",
180 8e67ea28 Christos Stavrakakis
                                  status="success",
181 8e67ea28 Christos Stavrakakis
                                  logmsg="Reconciled eventd: VM creation"
182 8e67ea28 Christos Stavrakakis
                                         " failed.")
183 41a7fae7 Christos Stavrakakis
        raise
184 41a7fae7 Christos Stavrakakis
185 41a7fae7 Christos Stavrakakis
    return vm
186 41a7fae7 Christos Stavrakakis
187 41a7fae7 Christos Stavrakakis
188 562bf712 Christos Stavrakakis
@transaction.commit_on_success
189 562bf712 Christos Stavrakakis
def allocate_new_server(userid, flavor):
190 562bf712 Christos Stavrakakis
    """Allocate a new server to a Ganeti backend.
191 562bf712 Christos Stavrakakis

192 562bf712 Christos Stavrakakis
    Allocation is performed based on the owner of the server and the specified
193 562bf712 Christos Stavrakakis
    flavor. Also, backends that do not have a public IPv4 address are excluded
194 562bf712 Christos Stavrakakis
    from server allocation.
195 562bf712 Christos Stavrakakis

196 562bf712 Christos Stavrakakis
    This function runs inside a transaction, because after allocating the
197 562bf712 Christos Stavrakakis
    instance a commit must be performed in order to release all locks.
198 562bf712 Christos Stavrakakis

199 562bf712 Christos Stavrakakis
    """
200 562bf712 Christos Stavrakakis
    backend_allocator = BackendAllocator()
201 562bf712 Christos Stavrakakis
    use_backend = backend_allocator.allocate(userid, flavor)
202 562bf712 Christos Stavrakakis
    if use_backend is None:
203 562bf712 Christos Stavrakakis
        log.error("No available backend for VM with flavor %s", flavor)
204 562bf712 Christos Stavrakakis
        raise faults.ServiceUnavailable("No available backends")
205 562bf712 Christos Stavrakakis
    return use_backend
206 562bf712 Christos Stavrakakis
207 562bf712 Christos Stavrakakis
208 562bf712 Christos Stavrakakis
@server_command("BUILD")
209 562bf712 Christos Stavrakakis
def create_server(vm, nics, flavor, image, personality, password):
210 562bf712 Christos Stavrakakis
    # dispatch server created signal needed to trigger the 'vmapi', which
211 562bf712 Christos Stavrakakis
    # enriches the vm object with the 'config_url' attribute which must be
212 562bf712 Christos Stavrakakis
    # passed to the Ganeti job.
213 562bf712 Christos Stavrakakis
    server_created.send(sender=vm, created_vm_params={
214 562bf712 Christos Stavrakakis
        'img_id': image['backend_id'],
215 562bf712 Christos Stavrakakis
        'img_passwd': password,
216 562bf712 Christos Stavrakakis
        'img_format': str(image['format']),
217 562bf712 Christos Stavrakakis
        'img_personality': json.dumps(personality),
218 562bf712 Christos Stavrakakis
        'img_properties': json.dumps(image['metadata']),
219 562bf712 Christos Stavrakakis
    })
220 562bf712 Christos Stavrakakis
    # send job to Ganeti
221 562bf712 Christos Stavrakakis
    jobID = backend.create_instance(vm, nics, flavor, image)
222 562bf712 Christos Stavrakakis
    # At this point the job is enqueued in the Ganeti backend
223 562bf712 Christos Stavrakakis
    vm.backendjobid = jobID
224 562bf712 Christos Stavrakakis
    vm.save()
225 562bf712 Christos Stavrakakis
    log.info("User %s created VM %s, NICs %s, Backend %s, JobID %s",
226 562bf712 Christos Stavrakakis
             vm.userid, vm, nics, backend, str(jobID))
227 562bf712 Christos Stavrakakis
228 562bf712 Christos Stavrakakis
    return jobID
229 562bf712 Christos Stavrakakis
230 562bf712 Christos Stavrakakis
231 816d7588 Christos Stavrakakis
def create_instance_nics(vm, userid, private_networks=[], floating_ips=[]):
232 cb66110b Christos Stavrakakis
    """Create NICs for VirtualMachine.
233 cb66110b Christos Stavrakakis

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

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

383 41a7fae7 Christos Stavrakakis
    This method arranges for an OOB console of the specified type.
384 41a7fae7 Christos Stavrakakis
    Only consoles of type "vnc" are supported for now.
385 41a7fae7 Christos Stavrakakis

386 41a7fae7 Christos Stavrakakis
    It uses a running instance of vncauthproxy to setup proper
387 41a7fae7 Christos Stavrakakis
    VNC forwarding with a random password, then returns the necessary
388 41a7fae7 Christos Stavrakakis
    VNC connection info to the caller.
389 41a7fae7 Christos Stavrakakis

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

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

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