Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / servers.py @ 1fb3293d

History | View | Annotate | Download (18 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 710b1c43 Christos Stavrakakis
from synnefo.db.models import (NetworkInterface, VirtualMachine,
16 710b1c43 Christos Stavrakakis
                               VirtualMachineMetadata, IPAddress)
17 710b1c43 Christos Stavrakakis
from synnefo.db import query as db_query, pools
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 88fd91af Christos Stavrakakis
            if action == "BUILD" and vm.serial is not None:
114 88fd91af Christos Stavrakakis
                # XXX: Special case for server creation: we must accept the
115 88fd91af Christos Stavrakakis
                # commission because the VM has been stored in DB. Also, if
116 88fd91af Christos Stavrakakis
                # communication with Ganeti fails, the job will never reach
117 88fd91af Christos Stavrakakis
                # Ganeti, and the commission will never be resolved.
118 88fd91af Christos Stavrakakis
                quotas.accept_serial(vm.serial)
119 88fd91af Christos Stavrakakis
120 41a7fae7 Christos Stavrakakis
            log.info("user: %s, vm: %s, action: %s, job_id: %s, serial: %s",
121 41a7fae7 Christos Stavrakakis
                     user_id, vm.id, action, job_id, vm.serial)
122 41a7fae7 Christos Stavrakakis
123 41a7fae7 Christos Stavrakakis
            # store the new task in the VM
124 41a7fae7 Christos Stavrakakis
            if job_id is not None:
125 41a7fae7 Christos Stavrakakis
                vm.task = action
126 41a7fae7 Christos Stavrakakis
                vm.task_job_id = job_id
127 41a7fae7 Christos Stavrakakis
            vm.save()
128 41a7fae7 Christos Stavrakakis
129 41a7fae7 Christos Stavrakakis
            return vm
130 41a7fae7 Christos Stavrakakis
        return wrapper
131 41a7fae7 Christos Stavrakakis
    return decorator
132 41a7fae7 Christos Stavrakakis
133 41a7fae7 Christos Stavrakakis
134 562bf712 Christos Stavrakakis
@transaction.commit_on_success
135 41a7fae7 Christos Stavrakakis
def create(userid, name, password, flavor, image, metadata={},
136 5aeb4e93 Christos Stavrakakis
           personality=[], private_networks=None, floating_ips=None,
137 5aeb4e93 Christos Stavrakakis
           use_backend=None):
138 41a7fae7 Christos Stavrakakis
    if use_backend is None:
139 562bf712 Christos Stavrakakis
        # Allocate server to a Ganeti backend
140 562bf712 Christos Stavrakakis
        use_backend = allocate_new_server(userid, flavor)
141 41a7fae7 Christos Stavrakakis
142 6193628f Christos Stavrakakis
    if private_networks is None:
143 6193628f Christos Stavrakakis
        private_networks = []
144 6193628f Christos Stavrakakis
    if floating_ips is None:
145 6193628f Christos Stavrakakis
        floating_ips = []
146 6193628f Christos Stavrakakis
147 41a7fae7 Christos Stavrakakis
    # Fix flavor for archipelago
148 41a7fae7 Christos Stavrakakis
    disk_template, provider = util.get_flavor_provider(flavor)
149 41a7fae7 Christos Stavrakakis
    if provider:
150 41a7fae7 Christos Stavrakakis
        flavor.disk_template = disk_template
151 41a7fae7 Christos Stavrakakis
        flavor.disk_provider = provider
152 41a7fae7 Christos Stavrakakis
        flavor.disk_origin = None
153 41a7fae7 Christos Stavrakakis
        if provider == 'vlmc':
154 41a7fae7 Christos Stavrakakis
            flavor.disk_origin = image['checksum']
155 41a7fae7 Christos Stavrakakis
            image['backend_id'] = 'null'
156 41a7fae7 Christos Stavrakakis
    else:
157 41a7fae7 Christos Stavrakakis
        flavor.disk_provider = None
158 41a7fae7 Christos Stavrakakis
159 562bf712 Christos Stavrakakis
    # We must save the VM instance now, so that it gets a valid
160 562bf712 Christos Stavrakakis
    # vm.backend_vm_id.
161 562bf712 Christos Stavrakakis
    vm = VirtualMachine.objects.create(name=name,
162 562bf712 Christos Stavrakakis
                                       backend=use_backend,
163 562bf712 Christos Stavrakakis
                                       userid=userid,
164 562bf712 Christos Stavrakakis
                                       imageid=image["id"],
165 562bf712 Christos Stavrakakis
                                       flavor=flavor,
166 562bf712 Christos Stavrakakis
                                       operstate="BUILD")
167 562bf712 Christos Stavrakakis
    log.info("Created entry in DB for VM '%s'", vm)
168 562bf712 Christos Stavrakakis
169 562bf712 Christos Stavrakakis
    nics = create_instance_nics(vm, userid, private_networks, floating_ips)
170 562bf712 Christos Stavrakakis
171 562bf712 Christos Stavrakakis
    for key, val in metadata.items():
172 562bf712 Christos Stavrakakis
        VirtualMachineMetadata.objects.create(
173 562bf712 Christos Stavrakakis
            meta_key=key,
174 562bf712 Christos Stavrakakis
            meta_value=val,
175 562bf712 Christos Stavrakakis
            vm=vm)
176 41a7fae7 Christos Stavrakakis
177 88fd91af Christos Stavrakakis
    # Create the server in Ganeti.
178 e5e62972 Giorgos Korfiatis
    vm = create_server(vm, nics, flavor, image, personality, password)
179 41a7fae7 Christos Stavrakakis
180 41a7fae7 Christos Stavrakakis
    return vm
181 41a7fae7 Christos Stavrakakis
182 41a7fae7 Christos Stavrakakis
183 562bf712 Christos Stavrakakis
@transaction.commit_on_success
184 562bf712 Christos Stavrakakis
def allocate_new_server(userid, flavor):
185 562bf712 Christos Stavrakakis
    """Allocate a new server to a Ganeti backend.
186 562bf712 Christos Stavrakakis

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

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

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

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

243 cb66110b Christos Stavrakakis
    """
244 710b1c43 Christos Stavrakakis
    nics = []
245 cb66110b Christos Stavrakakis
    for network_id in settings.DEFAULT_INSTANCE_NETWORKS:
246 9446e7e5 Christos Stavrakakis
        if network_id == "SNF:ANY_PUBLIC":
247 710b1c43 Christos Stavrakakis
            ipaddress = util.allocate_public_ip(userid=userid,
248 710b1c43 Christos Stavrakakis
                                                backend=vm.backend)
249 710b1c43 Christos Stavrakakis
            nic, ipaddress = create_nic(vm, ipaddress=ipaddress)
250 cb66110b Christos Stavrakakis
        else:
251 cb66110b Christos Stavrakakis
            try:
252 710b1c43 Christos Stavrakakis
                network = util.get_network(network_id, userid,
253 710b1c43 Christos Stavrakakis
                                           non_deleted=True)
254 710b1c43 Christos Stavrakakis
            except faults.ItemNotFound:
255 cb66110b Christos Stavrakakis
                msg = "Invalid configuration. Setting"\
256 cb66110b Christos Stavrakakis
                      " 'DEFAULT_INSTANCE_NETWORKS' contains invalid"\
257 cb66110b Christos Stavrakakis
                      " network '%s'" % network_id
258 cb66110b Christos Stavrakakis
                log.error(msg)
259 710b1c43 Christos Stavrakakis
                raise faults.InternalServerError(msg)
260 710b1c43 Christos Stavrakakis
            nic, ipaddress = create_nic(vm, network=network)
261 710b1c43 Christos Stavrakakis
        nics.append(nic)
262 816d7588 Christos Stavrakakis
    for address in floating_ips:
263 710b1c43 Christos Stavrakakis
        floating_ip = util.get_floating_ip_by_address(vm.userid, address,
264 710b1c43 Christos Stavrakakis
                                                      for_update=True)
265 710b1c43 Christos Stavrakakis
        nic, ipaddress = create_nic(vm, ipaddress=floating_ip)
266 710b1c43 Christos Stavrakakis
        nics.append(nic)
267 cb66110b Christos Stavrakakis
    for network_id in private_networks:
268 cb66110b Christos Stavrakakis
        network = util.get_network(network_id, userid, non_deleted=True)
269 cb66110b Christos Stavrakakis
        if network.public:
270 cb66110b Christos Stavrakakis
            raise faults.Forbidden("Can not connect to public network")
271 710b1c43 Christos Stavrakakis
        nic, ipaddress = create_nic(vm, network=network)
272 cb66110b Christos Stavrakakis
        nics.append(nic)
273 710b1c43 Christos Stavrakakis
    for index, nic in enumerate(nics):
274 710b1c43 Christos Stavrakakis
        nic.index = index
275 710b1c43 Christos Stavrakakis
        nic.save()
276 cb66110b Christos Stavrakakis
    return nics
277 cb66110b Christos Stavrakakis
278 cb66110b Christos Stavrakakis
279 710b1c43 Christos Stavrakakis
def create_nic(vm, network=None, ipaddress=None, address=None):
280 710b1c43 Christos Stavrakakis
    """Helper functions for create NIC objects.
281 710b1c43 Christos Stavrakakis

282 710b1c43 Christos Stavrakakis
    Create a NetworkInterface connecting a VirtualMachine to a network with the
283 710b1c43 Christos Stavrakakis
    IPAddress specified. If no 'ipaddress' is passed and the network has an
284 710b1c43 Christos Stavrakakis
    IPv4 subnet, then an IPv4 address will be automatically be allocated.
285 710b1c43 Christos Stavrakakis

286 710b1c43 Christos Stavrakakis
    """
287 710b1c43 Christos Stavrakakis
    userid = vm.userid
288 710b1c43 Christos Stavrakakis
289 710b1c43 Christos Stavrakakis
    if ipaddress is None:
290 710b1c43 Christos Stavrakakis
        if network.subnets.filter(ipversion=4).exists():
291 710b1c43 Christos Stavrakakis
            try:
292 710b1c43 Christos Stavrakakis
                ipaddress = util.allocate_ip(network, userid=userid,
293 710b1c43 Christos Stavrakakis
                                             address=address)
294 710b1c43 Christos Stavrakakis
            except pools.ValueNotAvailable:
295 710b1c43 Christos Stavrakakis
                raise faults.badRequest("Address '%s' is not available." %
296 710b1c43 Christos Stavrakakis
                                        address)
297 710b1c43 Christos Stavrakakis
298 710b1c43 Christos Stavrakakis
    if ipaddress is not None and ipaddress.nic is not None:
299 710b1c43 Christos Stavrakakis
        raise faults.Conflict("IP address '%s' already in use" %
300 710b1c43 Christos Stavrakakis
                              ipaddress.address)
301 710b1c43 Christos Stavrakakis
302 710b1c43 Christos Stavrakakis
    if network is None:
303 710b1c43 Christos Stavrakakis
        network = ipaddress.network
304 710b1c43 Christos Stavrakakis
    elif network.state != 'ACTIVE':
305 710b1c43 Christos Stavrakakis
        # TODO: What if is in settings ?
306 710b1c43 Christos Stavrakakis
        raise faults.BuildInProgress('Network not active yet')
307 710b1c43 Christos Stavrakakis
308 014df29c Christos Stavrakakis
    #device_owner = "router" if vm.router else "vm"
309 014df29c Christos Stavrakakis
    device_owner = "vm"
310 710b1c43 Christos Stavrakakis
    nic = NetworkInterface.objects.create(machine=vm, network=network,
311 3a6be177 Christos Stavrakakis
                                          state="BUILD",
312 54f13218 Christos Stavrakakis
                                          device_owner=device_owner)
313 710b1c43 Christos Stavrakakis
    if ipaddress is not None:
314 710b1c43 Christos Stavrakakis
        ipaddress.nic = nic
315 710b1c43 Christos Stavrakakis
        ipaddress.save()
316 710b1c43 Christos Stavrakakis
317 710b1c43 Christos Stavrakakis
    return nic, ipaddress
318 710b1c43 Christos Stavrakakis
319 710b1c43 Christos Stavrakakis
320 41a7fae7 Christos Stavrakakis
@server_command("DESTROY")
321 41a7fae7 Christos Stavrakakis
def destroy(vm):
322 41a7fae7 Christos Stavrakakis
    log.info("Deleting VM %s", vm)
323 41a7fae7 Christos Stavrakakis
    return backend.delete_instance(vm)
324 41a7fae7 Christos Stavrakakis
325 41a7fae7 Christos Stavrakakis
326 41a7fae7 Christos Stavrakakis
@server_command("START")
327 41a7fae7 Christos Stavrakakis
def start(vm):
328 41a7fae7 Christos Stavrakakis
    log.info("Starting VM %s", vm)
329 41a7fae7 Christos Stavrakakis
    return backend.startup_instance(vm)
330 41a7fae7 Christos Stavrakakis
331 41a7fae7 Christos Stavrakakis
332 41a7fae7 Christos Stavrakakis
@server_command("STOP")
333 41a7fae7 Christos Stavrakakis
def stop(vm):
334 41a7fae7 Christos Stavrakakis
    log.info("Stopping VM %s", vm)
335 41a7fae7 Christos Stavrakakis
    return backend.shutdown_instance(vm)
336 41a7fae7 Christos Stavrakakis
337 41a7fae7 Christos Stavrakakis
338 41a7fae7 Christos Stavrakakis
@server_command("REBOOT")
339 41a7fae7 Christos Stavrakakis
def reboot(vm, reboot_type):
340 41a7fae7 Christos Stavrakakis
    if reboot_type not in ("SOFT", "HARD"):
341 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Malformed request. Invalid reboot"
342 41a7fae7 Christos Stavrakakis
                                " type %s" % reboot_type)
343 41a7fae7 Christos Stavrakakis
    log.info("Rebooting VM %s. Type %s", vm, reboot_type)
344 41a7fae7 Christos Stavrakakis
345 41a7fae7 Christos Stavrakakis
    return backend.reboot_instance(vm, reboot_type.lower())
346 41a7fae7 Christos Stavrakakis
347 41a7fae7 Christos Stavrakakis
348 41a7fae7 Christos Stavrakakis
@server_command("RESIZE")
349 41a7fae7 Christos Stavrakakis
def resize(vm, flavor):
350 41a7fae7 Christos Stavrakakis
    old_flavor = vm.flavor
351 41a7fae7 Christos Stavrakakis
    # User requested the same flavor
352 41a7fae7 Christos Stavrakakis
    if old_flavor.id == flavor.id:
353 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Server '%s' flavor is already '%s'."
354 41a7fae7 Christos Stavrakakis
                                % (vm, flavor))
355 41a7fae7 Christos Stavrakakis
        return None
356 41a7fae7 Christos Stavrakakis
    # Check that resize can be performed
357 41a7fae7 Christos Stavrakakis
    if old_flavor.disk != flavor.disk:
358 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Can not resize instance disk.")
359 41a7fae7 Christos Stavrakakis
    if old_flavor.disk_template != flavor.disk_template:
360 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Can not change instance disk template.")
361 41a7fae7 Christos Stavrakakis
362 41a7fae7 Christos Stavrakakis
    log.info("Resizing VM from flavor '%s' to '%s", old_flavor, flavor)
363 41a7fae7 Christos Stavrakakis
    commission_info = {"cyclades.cpu": flavor.cpu - old_flavor.cpu,
364 f05a25af Christos Stavrakakis
                       "cyclades.ram": 1048576 * (flavor.ram - old_flavor.ram)}
365 41a7fae7 Christos Stavrakakis
    # Save serial to VM, since it is needed by server_command decorator
366 41a7fae7 Christos Stavrakakis
    vm.serial = quotas.issue_commission(user=vm.userid,
367 41a7fae7 Christos Stavrakakis
                                        source=quotas.DEFAULT_SOURCE,
368 9122ffab Christos Stavrakakis
                                        provisions=commission_info,
369 9122ffab Christos Stavrakakis
                                        name="resource: %s. resize" % vm)
370 41a7fae7 Christos Stavrakakis
    return backend.resize_instance(vm, vcpus=flavor.cpu, memory=flavor.ram)
371 41a7fae7 Christos Stavrakakis
372 41a7fae7 Christos Stavrakakis
373 41a7fae7 Christos Stavrakakis
@server_command("SET_FIREWALL_PROFILE")
374 d0545590 Christos Stavrakakis
def set_firewall_profile(vm, profile, nic):
375 d0545590 Christos Stavrakakis
    log.info("Setting VM %s, NIC %s, firewall %s", vm, nic, profile)
376 41a7fae7 Christos Stavrakakis
377 41a7fae7 Christos Stavrakakis
    if profile not in [x[0] for x in NetworkInterface.FIREWALL_PROFILES]:
378 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Unsupported firewall profile")
379 d0545590 Christos Stavrakakis
    backend.set_firewall_profile(vm, profile=profile, nic=nic)
380 41a7fae7 Christos Stavrakakis
    return None
381 41a7fae7 Christos Stavrakakis
382 41a7fae7 Christos Stavrakakis
383 41a7fae7 Christos Stavrakakis
@server_command("CONNECT")
384 41a7fae7 Christos Stavrakakis
def connect(vm, network):
385 710b1c43 Christos Stavrakakis
    nic, ipaddress = create_nic(vm, network)
386 41a7fae7 Christos Stavrakakis
387 710b1c43 Christos Stavrakakis
    log.info("Creating NIC %s with IPAddress %s", nic, ipaddress)
388 2a2b01e5 Christos Stavrakakis
389 2a2b01e5 Christos Stavrakakis
    return backend.connect_to_network(vm, nic)
390 41a7fae7 Christos Stavrakakis
391 41a7fae7 Christos Stavrakakis
392 41a7fae7 Christos Stavrakakis
@server_command("DISCONNECT")
393 7c714455 Christos Stavrakakis
def disconnect(vm, nic):
394 7c714455 Christos Stavrakakis
    log.info("Removing NIC %s from VM %s", nic, vm)
395 41a7fae7 Christos Stavrakakis
    return backend.disconnect_from_network(vm, nic)
396 41a7fae7 Christos Stavrakakis
397 41a7fae7 Christos Stavrakakis
398 41a7fae7 Christos Stavrakakis
def console(vm, console_type):
399 41a7fae7 Christos Stavrakakis
    """Arrange for an OOB console of the specified type
400 41a7fae7 Christos Stavrakakis

401 41a7fae7 Christos Stavrakakis
    This method arranges for an OOB console of the specified type.
402 41a7fae7 Christos Stavrakakis
    Only consoles of type "vnc" are supported for now.
403 41a7fae7 Christos Stavrakakis

404 41a7fae7 Christos Stavrakakis
    It uses a running instance of vncauthproxy to setup proper
405 41a7fae7 Christos Stavrakakis
    VNC forwarding with a random password, then returns the necessary
406 41a7fae7 Christos Stavrakakis
    VNC connection info to the caller.
407 41a7fae7 Christos Stavrakakis

408 41a7fae7 Christos Stavrakakis
    """
409 41a7fae7 Christos Stavrakakis
    log.info("Get console  VM %s, type %s", vm, console_type)
410 41a7fae7 Christos Stavrakakis
411 41a7fae7 Christos Stavrakakis
    # Use RAPI to get VNC console information for this instance
412 41a7fae7 Christos Stavrakakis
    if vm.operstate != "STARTED":
413 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest('Server not in ACTIVE state.')
414 41a7fae7 Christos Stavrakakis
415 41a7fae7 Christos Stavrakakis
    if settings.TEST:
416 41a7fae7 Christos Stavrakakis
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
417 41a7fae7 Christos Stavrakakis
    else:
418 41a7fae7 Christos Stavrakakis
        console_data = backend.get_instance_console(vm)
419 41a7fae7 Christos Stavrakakis
420 41a7fae7 Christos Stavrakakis
    if console_data['kind'] != 'vnc':
421 41a7fae7 Christos Stavrakakis
        message = 'got console of kind %s, not "vnc"' % console_data['kind']
422 41a7fae7 Christos Stavrakakis
        raise faults.ServiceUnavailable(message)
423 41a7fae7 Christos Stavrakakis
424 41a7fae7 Christos Stavrakakis
    # Let vncauthproxy decide on the source port.
425 41a7fae7 Christos Stavrakakis
    # The alternative: static allocation, e.g.
426 41a7fae7 Christos Stavrakakis
    # sport = console_data['port'] - 1000
427 41a7fae7 Christos Stavrakakis
    sport = 0
428 41a7fae7 Christos Stavrakakis
    daddr = console_data['host']
429 41a7fae7 Christos Stavrakakis
    dport = console_data['port']
430 41a7fae7 Christos Stavrakakis
    password = util.random_password()
431 41a7fae7 Christos Stavrakakis
432 41a7fae7 Christos Stavrakakis
    if settings.TEST:
433 41a7fae7 Christos Stavrakakis
        fwd = {'source_port': 1234, 'status': 'OK'}
434 41a7fae7 Christos Stavrakakis
    else:
435 41a7fae7 Christos Stavrakakis
        fwd = request_vnc_forwarding(sport, daddr, dport, password)
436 41a7fae7 Christos Stavrakakis
437 41a7fae7 Christos Stavrakakis
    if fwd['status'] != "OK":
438 41a7fae7 Christos Stavrakakis
        raise faults.ServiceUnavailable('vncauthproxy returned error status')
439 41a7fae7 Christos Stavrakakis
440 41a7fae7 Christos Stavrakakis
    # Verify that the VNC server settings haven't changed
441 41a7fae7 Christos Stavrakakis
    if not settings.TEST:
442 41a7fae7 Christos Stavrakakis
        if console_data != backend.get_instance_console(vm):
443 41a7fae7 Christos Stavrakakis
            raise faults.ServiceUnavailable('VNC Server settings changed.')
444 41a7fae7 Christos Stavrakakis
445 41a7fae7 Christos Stavrakakis
    console = {
446 41a7fae7 Christos Stavrakakis
        'type': 'vnc',
447 41a7fae7 Christos Stavrakakis
        'host': getfqdn(),
448 41a7fae7 Christos Stavrakakis
        'port': fwd['source_port'],
449 41a7fae7 Christos Stavrakakis
        'password': password}
450 41a7fae7 Christos Stavrakakis
451 41a7fae7 Christos Stavrakakis
    return console
452 9ba6bb95 Christos Stavrakakis
453 9ba6bb95 Christos Stavrakakis
454 f8675683 Christos Stavrakakis
@server_command("CONNECT")
455 9ba6bb95 Christos Stavrakakis
def add_floating_ip(vm, address):
456 710b1c43 Christos Stavrakakis
    # Use for_update, to guarantee that floating IP will only by assigned once
457 710b1c43 Christos Stavrakakis
    # and that it can not be released will it is being attached!
458 710b1c43 Christos Stavrakakis
    floating_ip = util.get_floating_ip_by_address(vm.userid, address,
459 710b1c43 Christos Stavrakakis
                                                  for_update=True)
460 710b1c43 Christos Stavrakakis
    nic, floating_ip = create_nic(vm, ipaddress=floating_ip)
461 710b1c43 Christos Stavrakakis
    log.info("Created NIC %s with floating IP %s", nic, floating_ip)
462 2a2b01e5 Christos Stavrakakis
    return backend.connect_to_network(vm, nic)
463 816d7588 Christos Stavrakakis
464 816d7588 Christos Stavrakakis
465 f8675683 Christos Stavrakakis
@server_command("DISCONNECT")
466 9ba6bb95 Christos Stavrakakis
def remove_floating_ip(vm, address):
467 9ba6bb95 Christos Stavrakakis
    try:
468 92d2d1ce Christos Stavrakakis
        floating_ip = db_query.get_server_floating_ip(server=vm,
469 92d2d1ce Christos Stavrakakis
                                                      address=address,
470 92d2d1ce Christos Stavrakakis
                                                      for_update=True)
471 326c3ec8 Christos Stavrakakis
    except IPAddress.DoesNotExist:
472 92d2d1ce Christos Stavrakakis
        raise faults.BadRequest("Server '%s' has no floating ip with"
473 92d2d1ce Christos Stavrakakis
                                " address '%s'" % (vm, address))
474 9ba6bb95 Christos Stavrakakis
475 92d2d1ce Christos Stavrakakis
    nic = floating_ip.nic
476 9ba6bb95 Christos Stavrakakis
    log.info("Removing NIC %s from VM %s. Floating IP '%s'", str(nic.index),
477 9ba6bb95 Christos Stavrakakis
             vm, floating_ip)
478 9ba6bb95 Christos Stavrakakis
479 9ba6bb95 Christos Stavrakakis
    return backend.disconnect_from_network(vm, nic)
480 a52cc1b4 Christos Stavrakakis
481 a52cc1b4 Christos Stavrakakis
482 a52cc1b4 Christos Stavrakakis
def rename(server, new_name):
483 a52cc1b4 Christos Stavrakakis
    """Rename a VirtualMachine."""
484 a52cc1b4 Christos Stavrakakis
    old_name = server.name
485 a52cc1b4 Christos Stavrakakis
    server.name = new_name
486 a52cc1b4 Christos Stavrakakis
    server.save()
487 a52cc1b4 Christos Stavrakakis
    log.info("Renamed server '%s' from '%s' to '%s'", server, old_name,
488 a52cc1b4 Christos Stavrakakis
             new_name)
489 a52cc1b4 Christos Stavrakakis
    return server