Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (25.3 kB)

1 91884d63 Giorgos Korfiatis
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
2 91884d63 Giorgos Korfiatis
#
3 91884d63 Giorgos Korfiatis
# Redistribution and use in source and binary forms, with or without
4 91884d63 Giorgos Korfiatis
# modification, are permitted provided that the following conditions
5 91884d63 Giorgos Korfiatis
# are met:
6 91884d63 Giorgos Korfiatis
#
7 91884d63 Giorgos Korfiatis
#   1. Redistributions of source code must retain the above copyright
8 91884d63 Giorgos Korfiatis
#      notice, this list of conditions and the following disclaimer.
9 91884d63 Giorgos Korfiatis
#
10 91884d63 Giorgos Korfiatis
#  2. Redistributions in binary form must reproduce the above copyright
11 91884d63 Giorgos Korfiatis
#     notice, this list of conditions and the following disclaimer in the
12 91884d63 Giorgos Korfiatis
#     documentation and/or other materials provided with the distribution.
13 91884d63 Giorgos Korfiatis
#
14 91884d63 Giorgos Korfiatis
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 91884d63 Giorgos Korfiatis
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 91884d63 Giorgos Korfiatis
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 91884d63 Giorgos Korfiatis
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 91884d63 Giorgos Korfiatis
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 91884d63 Giorgos Korfiatis
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 91884d63 Giorgos Korfiatis
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 91884d63 Giorgos Korfiatis
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 91884d63 Giorgos Korfiatis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 91884d63 Giorgos Korfiatis
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 91884d63 Giorgos Korfiatis
# SUCH DAMAGE.
25 91884d63 Giorgos Korfiatis
#
26 91884d63 Giorgos Korfiatis
# The views and conclusions contained in the software and documentation are
27 91884d63 Giorgos Korfiatis
# those of the authors and should not be interpreted as representing official
28 91884d63 Giorgos Korfiatis
# policies, either expressed or implied, of GRNET S.A.
29 91884d63 Giorgos Korfiatis
30 41a7fae7 Christos Stavrakakis
import logging
31 41a7fae7 Christos Stavrakakis
32 41a7fae7 Christos Stavrakakis
from socket import getfqdn
33 41a7fae7 Christos Stavrakakis
from django import dispatch
34 41a7fae7 Christos Stavrakakis
from django.db import transaction
35 41a7fae7 Christos Stavrakakis
from django.utils import simplejson as json
36 41a7fae7 Christos Stavrakakis
37 41a7fae7 Christos Stavrakakis
from snf_django.lib.api import faults
38 0c09b1c0 Christos Stavrakakis
from django.conf import settings
39 41a7fae7 Christos Stavrakakis
from synnefo.api import util
40 ba6ad346 Dionysis Grigoropoulos
from synnefo.logic import backend, ips, utils
41 41a7fae7 Christos Stavrakakis
from synnefo.logic.backend_allocator import BackendAllocator
42 710b1c43 Christos Stavrakakis
from synnefo.db.models import (NetworkInterface, VirtualMachine,
43 5d805533 Christos Stavrakakis
                               VirtualMachineMetadata, IPAddressLog, Network)
44 41a7fae7 Christos Stavrakakis
from vncauthproxy.client import request_forwarding as request_vnc_forwarding
45 af1832fe Christos Stavrakakis
from synnefo.logic import rapi
46 5d805533 Christos Stavrakakis
from synnefo.volume.volumes import _create_volume
47 5d805533 Christos Stavrakakis
from synnefo.volume.util import get_volume
48 5d805533 Christos Stavrakakis
from synnefo.logic import commands
49 41a7fae7 Christos Stavrakakis
50 41a7fae7 Christos Stavrakakis
log = logging.getLogger(__name__)
51 41a7fae7 Christos Stavrakakis
52 41a7fae7 Christos Stavrakakis
# server creation signal
53 41a7fae7 Christos Stavrakakis
server_created = dispatch.Signal(providing_args=["created_vm_params"])
54 41a7fae7 Christos Stavrakakis
55 41a7fae7 Christos Stavrakakis
56 562bf712 Christos Stavrakakis
@transaction.commit_on_success
57 5d805533 Christos Stavrakakis
def create(userid, name, password, flavor, image_id, metadata={},
58 5d805533 Christos Stavrakakis
           personality=[], networks=None, volumes=None,
59 5d805533 Christos Stavrakakis
           use_backend=None):
60 5d805533 Christos Stavrakakis
61 5d805533 Christos Stavrakakis
    # Get image information
62 5d805533 Christos Stavrakakis
    # TODO: Image is not mandatory if disks are specified
63 5d805533 Christos Stavrakakis
    image = util.get_image_dict(image_id, userid)
64 ae835e3b Christos Stavrakakis
65 ae835e3b Christos Stavrakakis
    # Check that image fits into the disk
66 5d805533 Christos Stavrakakis
    if int(image["size"]) > (flavor.disk << 30):
67 5d805533 Christos Stavrakakis
        msg = ("Flavor's disk size '%s' is smaller than the image's"
68 5d805533 Christos Stavrakakis
               "size '%s'" % (flavor.disk << 30, image["size"]))
69 5d805533 Christos Stavrakakis
        raise faults.BadRequest(msg)
70 ae835e3b Christos Stavrakakis
71 41a7fae7 Christos Stavrakakis
    if use_backend is None:
72 562bf712 Christos Stavrakakis
        # Allocate server to a Ganeti backend
73 562bf712 Christos Stavrakakis
        use_backend = allocate_new_server(userid, flavor)
74 41a7fae7 Christos Stavrakakis
75 ba6ad346 Dionysis Grigoropoulos
    utils.check_name_length(name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH,
76 ba6ad346 Dionysis Grigoropoulos
                            "Server name is too long")
77 ba6ad346 Dionysis Grigoropoulos
78 3aecadc8 Christos Stavrakakis
    # Create the ports for the server
79 16a7ced5 Christos Stavrakakis
    ports = create_instance_ports(userid, networks)
80 6193628f Christos Stavrakakis
81 562bf712 Christos Stavrakakis
    # We must save the VM instance now, so that it gets a valid
82 562bf712 Christos Stavrakakis
    # vm.backend_vm_id.
83 562bf712 Christos Stavrakakis
    vm = VirtualMachine.objects.create(name=name,
84 562bf712 Christos Stavrakakis
                                       backend=use_backend,
85 562bf712 Christos Stavrakakis
                                       userid=userid,
86 562bf712 Christos Stavrakakis
                                       imageid=image["id"],
87 562bf712 Christos Stavrakakis
                                       flavor=flavor,
88 562bf712 Christos Stavrakakis
                                       operstate="BUILD")
89 562bf712 Christos Stavrakakis
    log.info("Created entry in DB for VM '%s'", vm)
90 562bf712 Christos Stavrakakis
91 3aecadc8 Christos Stavrakakis
    # Associate the ports with the server
92 3aecadc8 Christos Stavrakakis
    for index, port in enumerate(ports):
93 3aecadc8 Christos Stavrakakis
        associate_port_with_machine(port, vm)
94 3aecadc8 Christos Stavrakakis
        port.index = index
95 3aecadc8 Christos Stavrakakis
        port.save()
96 562bf712 Christos Stavrakakis
97 5d805533 Christos Stavrakakis
    # If no volumes are specified, we automatically create a volume with the
98 5d805533 Christos Stavrakakis
    # size of the flavor and filled with the specified image.
99 5d805533 Christos Stavrakakis
    if not volumes:
100 5d805533 Christos Stavrakakis
        volumes = [{"source_type": "image",
101 5d805533 Christos Stavrakakis
                    "source_uuid": image["id"],
102 5d805533 Christos Stavrakakis
                    "size": flavor.disk,
103 5d805533 Christos Stavrakakis
                    "delete_on_termination": True}]
104 5d805533 Christos Stavrakakis
105 5d805533 Christos Stavrakakis
    assert(len(volumes) > 0), "Cannot create server without volumes"
106 5d805533 Christos Stavrakakis
107 5d805533 Christos Stavrakakis
    if volumes[0]["source_type"] == "blank":
108 5d805533 Christos Stavrakakis
        raise faults.BadRequest("Root volume cannot be blank")
109 5d805533 Christos Stavrakakis
110 5d805533 Christos Stavrakakis
    server_volumes = []
111 5d805533 Christos Stavrakakis
    for index, vol_info in enumerate(volumes):
112 5d805533 Christos Stavrakakis
        if vol_info["source_type"] == "volume":
113 5d805533 Christos Stavrakakis
            uuid = vol_info["source_uuid"]
114 5d805533 Christos Stavrakakis
            v = get_volume(userid, uuid, for_update=True,
115 5d805533 Christos Stavrakakis
                           exception=faults.BadRequest)
116 5d805533 Christos Stavrakakis
            if v.status != "AVAILABLE":
117 5d805533 Christos Stavrakakis
                raise faults.BadRequest("Cannot use volume while it is in %s"
118 5d805533 Christos Stavrakakis
                                        " status" % v.status)
119 5d805533 Christos Stavrakakis
            v.delete_on_termination = vol_info["delete_on_termination"]
120 5d805533 Christos Stavrakakis
            v.index = index
121 5d805533 Christos Stavrakakis
            v.save()
122 5d805533 Christos Stavrakakis
        else:
123 5d805533 Christos Stavrakakis
            v = _create_volume(server=vm, user_id=userid,
124 5d805533 Christos Stavrakakis
                               index=index, **vol_info)
125 5d805533 Christos Stavrakakis
        server_volumes.append(v)
126 e7953d63 Christos Stavrakakis
127 562bf712 Christos Stavrakakis
    for key, val in metadata.items():
128 562bf712 Christos Stavrakakis
        VirtualMachineMetadata.objects.create(
129 562bf712 Christos Stavrakakis
            meta_key=key,
130 562bf712 Christos Stavrakakis
            meta_value=val,
131 562bf712 Christos Stavrakakis
            vm=vm)
132 41a7fae7 Christos Stavrakakis
133 88fd91af Christos Stavrakakis
    # Create the server in Ganeti.
134 5d805533 Christos Stavrakakis
    vm = create_server(vm, ports, server_volumes, flavor, image, personality,
135 e7953d63 Christos Stavrakakis
                       password)
136 41a7fae7 Christos Stavrakakis
137 41a7fae7 Christos Stavrakakis
    return vm
138 41a7fae7 Christos Stavrakakis
139 41a7fae7 Christos Stavrakakis
140 562bf712 Christos Stavrakakis
@transaction.commit_on_success
141 562bf712 Christos Stavrakakis
def allocate_new_server(userid, flavor):
142 562bf712 Christos Stavrakakis
    """Allocate a new server to a Ganeti backend.
143 562bf712 Christos Stavrakakis

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

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

151 562bf712 Christos Stavrakakis
    """
152 562bf712 Christos Stavrakakis
    backend_allocator = BackendAllocator()
153 562bf712 Christos Stavrakakis
    use_backend = backend_allocator.allocate(userid, flavor)
154 562bf712 Christos Stavrakakis
    if use_backend is None:
155 562bf712 Christos Stavrakakis
        log.error("No available backend for VM with flavor %s", flavor)
156 562bf712 Christos Stavrakakis
        raise faults.ServiceUnavailable("No available backends")
157 562bf712 Christos Stavrakakis
    return use_backend
158 562bf712 Christos Stavrakakis
159 562bf712 Christos Stavrakakis
160 5d805533 Christos Stavrakakis
@commands.server_command("BUILD")
161 e7953d63 Christos Stavrakakis
def create_server(vm, nics, volumes, flavor, image, personality, password):
162 562bf712 Christos Stavrakakis
    # dispatch server created signal needed to trigger the 'vmapi', which
163 562bf712 Christos Stavrakakis
    # enriches the vm object with the 'config_url' attribute which must be
164 562bf712 Christos Stavrakakis
    # passed to the Ganeti job.
165 5d805533 Christos Stavrakakis
166 5d805533 Christos Stavrakakis
    # If the root volume has a provider, then inform snf-image to not fill
167 5d805533 Christos Stavrakakis
    # the volume with data
168 5d805533 Christos Stavrakakis
    image_id = image["backend_id"]
169 5d805533 Christos Stavrakakis
    root_volume = volumes[0]
170 5d805533 Christos Stavrakakis
    if root_volume.origin is not None:
171 5d805533 Christos Stavrakakis
        image_id = "null"
172 5d805533 Christos Stavrakakis
173 562bf712 Christos Stavrakakis
    server_created.send(sender=vm, created_vm_params={
174 5d805533 Christos Stavrakakis
        'img_id': image_id,
175 562bf712 Christos Stavrakakis
        'img_passwd': password,
176 562bf712 Christos Stavrakakis
        'img_format': str(image['format']),
177 562bf712 Christos Stavrakakis
        'img_personality': json.dumps(personality),
178 562bf712 Christos Stavrakakis
        'img_properties': json.dumps(image['metadata']),
179 562bf712 Christos Stavrakakis
    })
180 562bf712 Christos Stavrakakis
    # send job to Ganeti
181 88fd91af Christos Stavrakakis
    try:
182 e7953d63 Christos Stavrakakis
        jobID = backend.create_instance(vm, nics, volumes, flavor, image)
183 88fd91af Christos Stavrakakis
    except:
184 88fd91af Christos Stavrakakis
        log.exception("Failed create instance '%s'", vm)
185 88fd91af Christos Stavrakakis
        jobID = None
186 88fd91af Christos Stavrakakis
        vm.operstate = "ERROR"
187 88fd91af Christos Stavrakakis
        vm.backendlogmsg = "Failed to send job to Ganeti."
188 88fd91af Christos Stavrakakis
        vm.save()
189 88fd91af Christos Stavrakakis
        vm.nics.all().update(state="ERROR")
190 88fd91af Christos Stavrakakis
191 562bf712 Christos Stavrakakis
    # At this point the job is enqueued in the Ganeti backend
192 80a548e3 Christos Stavrakakis
    vm.backendopcode = "OP_INSTANCE_CREATE"
193 562bf712 Christos Stavrakakis
    vm.backendjobid = jobID
194 562bf712 Christos Stavrakakis
    vm.save()
195 562bf712 Christos Stavrakakis
    log.info("User %s created VM %s, NICs %s, Backend %s, JobID %s",
196 2fa6faca Christos Stavrakakis
             vm.userid, vm, nics, vm.backend, str(jobID))
197 562bf712 Christos Stavrakakis
198 562bf712 Christos Stavrakakis
    return jobID
199 562bf712 Christos Stavrakakis
200 562bf712 Christos Stavrakakis
201 5d805533 Christos Stavrakakis
@commands.server_command("DESTROY")
202 c397dbce Christos Stavrakakis
def destroy(vm, shutdown_timeout=None):
203 80a548e3 Christos Stavrakakis
    # XXX: Workaround for race where OP_INSTANCE_REMOVE starts executing on
204 80a548e3 Christos Stavrakakis
    # Ganeti before OP_INSTANCE_CREATE. This will be fixed when
205 80a548e3 Christos Stavrakakis
    # OP_INSTANCE_REMOVE supports the 'depends' request attribute.
206 80a548e3 Christos Stavrakakis
    if (vm.backendopcode == "OP_INSTANCE_CREATE" and
207 af1832fe Christos Stavrakakis
       vm.backendjobstatus not in rapi.JOB_STATUS_FINALIZED and
208 80a548e3 Christos Stavrakakis
       backend.job_is_still_running(vm) and
209 80a548e3 Christos Stavrakakis
       not backend.vm_exists_in_backend(vm)):
210 80a548e3 Christos Stavrakakis
            raise faults.BuildInProgress("Server is being build")
211 41a7fae7 Christos Stavrakakis
    log.info("Deleting VM %s", vm)
212 c915a198 Giorgos Korfiatis
    return backend.delete_instance(vm, shutdown_timeout=shutdown_timeout)
213 41a7fae7 Christos Stavrakakis
214 41a7fae7 Christos Stavrakakis
215 5d805533 Christos Stavrakakis
@commands.server_command("START")
216 41a7fae7 Christos Stavrakakis
def start(vm):
217 41a7fae7 Christos Stavrakakis
    log.info("Starting VM %s", vm)
218 41a7fae7 Christos Stavrakakis
    return backend.startup_instance(vm)
219 41a7fae7 Christos Stavrakakis
220 41a7fae7 Christos Stavrakakis
221 5d805533 Christos Stavrakakis
@commands.server_command("STOP")
222 c397dbce Christos Stavrakakis
def stop(vm, shutdown_timeout=None):
223 41a7fae7 Christos Stavrakakis
    log.info("Stopping VM %s", vm)
224 c915a198 Giorgos Korfiatis
    return backend.shutdown_instance(vm, shutdown_timeout=shutdown_timeout)
225 41a7fae7 Christos Stavrakakis
226 41a7fae7 Christos Stavrakakis
227 5d805533 Christos Stavrakakis
@commands.server_command("REBOOT")
228 c397dbce Christos Stavrakakis
def reboot(vm, reboot_type, shutdown_timeout=None):
229 41a7fae7 Christos Stavrakakis
    if reboot_type not in ("SOFT", "HARD"):
230 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Malformed request. Invalid reboot"
231 41a7fae7 Christos Stavrakakis
                                " type %s" % reboot_type)
232 41a7fae7 Christos Stavrakakis
    log.info("Rebooting VM %s. Type %s", vm, reboot_type)
233 41a7fae7 Christos Stavrakakis
234 c397dbce Christos Stavrakakis
    return backend.reboot_instance(vm, reboot_type.lower(),
235 c397dbce Christos Stavrakakis
                                   shutdown_timeout=shutdown_timeout)
236 41a7fae7 Christos Stavrakakis
237 41a7fae7 Christos Stavrakakis
238 41a7fae7 Christos Stavrakakis
def resize(vm, flavor):
239 64bca363 Giorgos Korfiatis
    action_fields = {"beparams": {"vcpus": flavor.cpu,
240 64bca363 Giorgos Korfiatis
                                  "maxmem": flavor.ram}}
241 5d805533 Christos Stavrakakis
    comm = commands.server_command("RESIZE", action_fields=action_fields)
242 64bca363 Giorgos Korfiatis
    return comm(_resize)(vm, flavor)
243 64bca363 Giorgos Korfiatis
244 64bca363 Giorgos Korfiatis
245 64bca363 Giorgos Korfiatis
def _resize(vm, flavor):
246 41a7fae7 Christos Stavrakakis
    old_flavor = vm.flavor
247 41a7fae7 Christos Stavrakakis
    # User requested the same flavor
248 41a7fae7 Christos Stavrakakis
    if old_flavor.id == flavor.id:
249 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Server '%s' flavor is already '%s'."
250 41a7fae7 Christos Stavrakakis
                                % (vm, flavor))
251 41a7fae7 Christos Stavrakakis
    # Check that resize can be performed
252 41a7fae7 Christos Stavrakakis
    if old_flavor.disk != flavor.disk:
253 8d5795b4 Christos Stavrakakis
        raise faults.BadRequest("Cannot resize instance disk.")
254 41a7fae7 Christos Stavrakakis
    if old_flavor.disk_template != flavor.disk_template:
255 8d5795b4 Christos Stavrakakis
        raise faults.BadRequest("Cannot change instance disk template.")
256 41a7fae7 Christos Stavrakakis
257 41a7fae7 Christos Stavrakakis
    log.info("Resizing VM from flavor '%s' to '%s", old_flavor, flavor)
258 41a7fae7 Christos Stavrakakis
    return backend.resize_instance(vm, vcpus=flavor.cpu, memory=flavor.ram)
259 41a7fae7 Christos Stavrakakis
260 41a7fae7 Christos Stavrakakis
261 5d805533 Christos Stavrakakis
@commands.server_command("SET_FIREWALL_PROFILE")
262 d0545590 Christos Stavrakakis
def set_firewall_profile(vm, profile, nic):
263 d0545590 Christos Stavrakakis
    log.info("Setting VM %s, NIC %s, firewall %s", vm, nic, profile)
264 41a7fae7 Christos Stavrakakis
265 41a7fae7 Christos Stavrakakis
    if profile not in [x[0] for x in NetworkInterface.FIREWALL_PROFILES]:
266 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Unsupported firewall profile")
267 d0545590 Christos Stavrakakis
    backend.set_firewall_profile(vm, profile=profile, nic=nic)
268 41a7fae7 Christos Stavrakakis
    return None
269 41a7fae7 Christos Stavrakakis
270 41a7fae7 Christos Stavrakakis
271 5d805533 Christos Stavrakakis
@commands.server_command("CONNECT")
272 6b8dc47c Christos Stavrakakis
def connect(vm, network, port=None):
273 6b8dc47c Christos Stavrakakis
    if port is None:
274 fae6e5f0 Christos Stavrakakis
        port = _create_port(vm.userid, network)
275 fae6e5f0 Christos Stavrakakis
    associate_port_with_machine(port, vm)
276 41a7fae7 Christos Stavrakakis
277 fae6e5f0 Christos Stavrakakis
    log.info("Creating NIC %s with IPv4 Address %s", port, port.ipv4_address)
278 41a7fae7 Christos Stavrakakis
279 fae6e5f0 Christos Stavrakakis
    return backend.connect_to_network(vm, port)
280 41a7fae7 Christos Stavrakakis
281 41a7fae7 Christos Stavrakakis
282 5d805533 Christos Stavrakakis
@commands.server_command("DISCONNECT")
283 7c714455 Christos Stavrakakis
def disconnect(vm, nic):
284 7c714455 Christos Stavrakakis
    log.info("Removing NIC %s from VM %s", nic, vm)
285 41a7fae7 Christos Stavrakakis
    return backend.disconnect_from_network(vm, nic)
286 41a7fae7 Christos Stavrakakis
287 41a7fae7 Christos Stavrakakis
288 41a7fae7 Christos Stavrakakis
def console(vm, console_type):
289 41a7fae7 Christos Stavrakakis
    """Arrange for an OOB console of the specified type
290 41a7fae7 Christos Stavrakakis

291 41a7fae7 Christos Stavrakakis
    This method arranges for an OOB console of the specified type.
292 41a7fae7 Christos Stavrakakis
    Only consoles of type "vnc" are supported for now.
293 41a7fae7 Christos Stavrakakis

294 41a7fae7 Christos Stavrakakis
    It uses a running instance of vncauthproxy to setup proper
295 41a7fae7 Christos Stavrakakis
    VNC forwarding with a random password, then returns the necessary
296 41a7fae7 Christos Stavrakakis
    VNC connection info to the caller.
297 41a7fae7 Christos Stavrakakis

298 41a7fae7 Christos Stavrakakis
    """
299 41a7fae7 Christos Stavrakakis
    log.info("Get console  VM %s, type %s", vm, console_type)
300 41a7fae7 Christos Stavrakakis
301 41a7fae7 Christos Stavrakakis
    # Use RAPI to get VNC console information for this instance
302 41a7fae7 Christos Stavrakakis
    if vm.operstate != "STARTED":
303 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest('Server not in ACTIVE state.')
304 41a7fae7 Christos Stavrakakis
305 41a7fae7 Christos Stavrakakis
    if settings.TEST:
306 41a7fae7 Christos Stavrakakis
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
307 41a7fae7 Christos Stavrakakis
    else:
308 41a7fae7 Christos Stavrakakis
        console_data = backend.get_instance_console(vm)
309 41a7fae7 Christos Stavrakakis
310 41a7fae7 Christos Stavrakakis
    if console_data['kind'] != 'vnc':
311 41a7fae7 Christos Stavrakakis
        message = 'got console of kind %s, not "vnc"' % console_data['kind']
312 41a7fae7 Christos Stavrakakis
        raise faults.ServiceUnavailable(message)
313 41a7fae7 Christos Stavrakakis
314 41a7fae7 Christos Stavrakakis
    # Let vncauthproxy decide on the source port.
315 41a7fae7 Christos Stavrakakis
    # The alternative: static allocation, e.g.
316 41a7fae7 Christos Stavrakakis
    # sport = console_data['port'] - 1000
317 41a7fae7 Christos Stavrakakis
    sport = 0
318 41a7fae7 Christos Stavrakakis
    daddr = console_data['host']
319 41a7fae7 Christos Stavrakakis
    dport = console_data['port']
320 41a7fae7 Christos Stavrakakis
    password = util.random_password()
321 41a7fae7 Christos Stavrakakis
322 41a7fae7 Christos Stavrakakis
    if settings.TEST:
323 41a7fae7 Christos Stavrakakis
        fwd = {'source_port': 1234, 'status': 'OK'}
324 41a7fae7 Christos Stavrakakis
    else:
325 f3c5f1df Stratos Psomadakis
        vnc_extra_opts = settings.CYCLADES_VNCAUTHPROXY_OPTS
326 f3c5f1df Stratos Psomadakis
        fwd = request_vnc_forwarding(sport, daddr, dport, password,
327 f3c5f1df Stratos Psomadakis
                                     **vnc_extra_opts)
328 41a7fae7 Christos Stavrakakis
329 41a7fae7 Christos Stavrakakis
    if fwd['status'] != "OK":
330 41a7fae7 Christos Stavrakakis
        raise faults.ServiceUnavailable('vncauthproxy returned error status')
331 41a7fae7 Christos Stavrakakis
332 41a7fae7 Christos Stavrakakis
    # Verify that the VNC server settings haven't changed
333 41a7fae7 Christos Stavrakakis
    if not settings.TEST:
334 41a7fae7 Christos Stavrakakis
        if console_data != backend.get_instance_console(vm):
335 41a7fae7 Christos Stavrakakis
            raise faults.ServiceUnavailable('VNC Server settings changed.')
336 41a7fae7 Christos Stavrakakis
337 41a7fae7 Christos Stavrakakis
    console = {
338 41a7fae7 Christos Stavrakakis
        'type': 'vnc',
339 41a7fae7 Christos Stavrakakis
        'host': getfqdn(),
340 41a7fae7 Christos Stavrakakis
        'port': fwd['source_port'],
341 41a7fae7 Christos Stavrakakis
        'password': password}
342 41a7fae7 Christos Stavrakakis
343 41a7fae7 Christos Stavrakakis
    return console
344 9ba6bb95 Christos Stavrakakis
345 9ba6bb95 Christos Stavrakakis
346 a52cc1b4 Christos Stavrakakis
def rename(server, new_name):
347 a52cc1b4 Christos Stavrakakis
    """Rename a VirtualMachine."""
348 a52cc1b4 Christos Stavrakakis
    old_name = server.name
349 a52cc1b4 Christos Stavrakakis
    server.name = new_name
350 a52cc1b4 Christos Stavrakakis
    server.save()
351 a52cc1b4 Christos Stavrakakis
    log.info("Renamed server '%s' from '%s' to '%s'", server, old_name,
352 a52cc1b4 Christos Stavrakakis
             new_name)
353 a52cc1b4 Christos Stavrakakis
    return server
354 816d7588 Christos Stavrakakis
355 816d7588 Christos Stavrakakis
356 fae6e5f0 Christos Stavrakakis
@transaction.commit_on_success
357 fae6e5f0 Christos Stavrakakis
def create_port(*args, **kwargs):
358 129b94d5 Christos Stavrakakis
    vm = kwargs.get("machine", None)
359 129b94d5 Christos Stavrakakis
    if vm is None and len(args) >= 3:
360 129b94d5 Christos Stavrakakis
        vm = args[2]
361 129b94d5 Christos Stavrakakis
    if vm is not None:
362 129b94d5 Christos Stavrakakis
        if vm.nics.count() == settings.GANETI_MAX_NICS_PER_INSTANCE:
363 129b94d5 Christos Stavrakakis
            raise faults.BadRequest("Maximum ports per server limit reached")
364 fae6e5f0 Christos Stavrakakis
    return _create_port(*args, **kwargs)
365 fae6e5f0 Christos Stavrakakis
366 fae6e5f0 Christos Stavrakakis
367 fae6e5f0 Christos Stavrakakis
def _create_port(userid, network, machine=None, use_ipaddress=None,
368 fae6e5f0 Christos Stavrakakis
                 address=None, name="", security_groups=None,
369 fae6e5f0 Christos Stavrakakis
                 device_owner=None):
370 fae6e5f0 Christos Stavrakakis
    """Create a new port on the specified network.
371 816d7588 Christos Stavrakakis

372 fae6e5f0 Christos Stavrakakis
    Create a new Port(NetworkInterface model) on the specified Network. If
373 fae6e5f0 Christos Stavrakakis
    'machine' is specified, the machine will be connected to the network using
374 fae6e5f0 Christos Stavrakakis
    this port. If 'use_ipaddress' argument is specified, the port will be
375 fae6e5f0 Christos Stavrakakis
    assigned this IPAddress. Otherwise, an IPv4 address from the IPv4 subnet
376 fae6e5f0 Christos Stavrakakis
    will be allocated.
377 816d7588 Christos Stavrakakis

378 816d7588 Christos Stavrakakis
    """
379 fae6e5f0 Christos Stavrakakis
    if network.state != "ACTIVE":
380 8f335041 Christos Stavrakakis
        raise faults.Conflict("Cannot create port while network '%s' is in"
381 8f335041 Christos Stavrakakis
                              " '%s' status" % (network.id, network.state))
382 8f335041 Christos Stavrakakis
    elif network.action == "DESTROY":
383 8d5795b4 Christos Stavrakakis
        msg = "Cannot create port. Network %s is being deleted."
384 3f18f035 Christos Stavrakakis
        raise faults.Conflict(msg % network.id)
385 32b1ed4a Christos Stavrakakis
    elif network.drained:
386 32b1ed4a Christos Stavrakakis
        raise faults.Conflict("Cannot create port while network %s is in"
387 32b1ed4a Christos Stavrakakis
                              " 'SNF:DRAINED' status" % network.id)
388 8f335041 Christos Stavrakakis
389 ba6ad346 Dionysis Grigoropoulos
    utils.check_name_length(name, NetworkInterface.NETWORK_IFACE_NAME_LENGTH,
390 ba6ad346 Dionysis Grigoropoulos
                            "Port name is too long")
391 ba6ad346 Dionysis Grigoropoulos
392 fae6e5f0 Christos Stavrakakis
    ipaddress = None
393 fae6e5f0 Christos Stavrakakis
    if use_ipaddress is not None:
394 fae6e5f0 Christos Stavrakakis
        # Use an existing IPAddress object.
395 fae6e5f0 Christos Stavrakakis
        ipaddress = use_ipaddress
396 fae6e5f0 Christos Stavrakakis
        if ipaddress and (ipaddress.network_id != network.id):
397 fae6e5f0 Christos Stavrakakis
            msg = "IP Address %s does not belong to network %s"
398 fae6e5f0 Christos Stavrakakis
            raise faults.Conflict(msg % (ipaddress.address, network.id))
399 fae6e5f0 Christos Stavrakakis
    else:
400 fae6e5f0 Christos Stavrakakis
        # If network has IPv4 subnets, try to allocate the address that the
401 fae6e5f0 Christos Stavrakakis
        # the user specified or a random one.
402 fae6e5f0 Christos Stavrakakis
        if network.subnets.filter(ipversion=4).exists():
403 0292883e Christos Stavrakakis
            ipaddress = ips.allocate_ip(network, userid=userid,
404 0292883e Christos Stavrakakis
                                        address=address)
405 fae6e5f0 Christos Stavrakakis
        elif address is not None:
406 fae6e5f0 Christos Stavrakakis
            raise faults.BadRequest("Address %s is not a valid IP for the"
407 fae6e5f0 Christos Stavrakakis
                                    " defined network subnets" % address)
408 fae6e5f0 Christos Stavrakakis
409 fae6e5f0 Christos Stavrakakis
    if ipaddress is not None and ipaddress.nic is not None:
410 fae6e5f0 Christos Stavrakakis
        raise faults.Conflict("IP address '%s' is already in use" %
411 fae6e5f0 Christos Stavrakakis
                              ipaddress.address)
412 fae6e5f0 Christos Stavrakakis
413 fae6e5f0 Christos Stavrakakis
    port = NetworkInterface.objects.create(network=network,
414 fae6e5f0 Christos Stavrakakis
                                           state="DOWN",
415 fae6e5f0 Christos Stavrakakis
                                           userid=userid,
416 fae6e5f0 Christos Stavrakakis
                                           device_owner=None,
417 fae6e5f0 Christos Stavrakakis
                                           name=name)
418 fae6e5f0 Christos Stavrakakis
419 fae6e5f0 Christos Stavrakakis
    # add the security groups if any
420 fae6e5f0 Christos Stavrakakis
    if security_groups:
421 fae6e5f0 Christos Stavrakakis
        port.security_groups.add(*security_groups)
422 fae6e5f0 Christos Stavrakakis
423 fae6e5f0 Christos Stavrakakis
    if ipaddress is not None:
424 fae6e5f0 Christos Stavrakakis
        # Associate IPAddress with the Port
425 fae6e5f0 Christos Stavrakakis
        ipaddress.nic = port
426 fae6e5f0 Christos Stavrakakis
        ipaddress.save()
427 fae6e5f0 Christos Stavrakakis
428 fae6e5f0 Christos Stavrakakis
    if machine is not None:
429 ae2da8a2 Christos Stavrakakis
        # Connect port to the instance.
430 fae6e5f0 Christos Stavrakakis
        machine = connect(machine, network, port)
431 fae6e5f0 Christos Stavrakakis
        jobID = machine.task_job_id
432 fae6e5f0 Christos Stavrakakis
        log.info("Created Port %s with IP %s. Ganeti Job: %s",
433 fae6e5f0 Christos Stavrakakis
                 port, ipaddress, jobID)
434 fae6e5f0 Christos Stavrakakis
    else:
435 fae6e5f0 Christos Stavrakakis
        log.info("Created Port %s with IP %s not attached to any instance",
436 fae6e5f0 Christos Stavrakakis
                 port, ipaddress)
437 9ba6bb95 Christos Stavrakakis
438 fae6e5f0 Christos Stavrakakis
    return port
439 9ba6bb95 Christos Stavrakakis
440 e7f74e7d Christos Stavrakakis
441 fae6e5f0 Christos Stavrakakis
def associate_port_with_machine(port, machine):
442 fae6e5f0 Christos Stavrakakis
    """Associate a Port with a VirtualMachine.
443 9ba6bb95 Christos Stavrakakis

444 fae6e5f0 Christos Stavrakakis
    Associate the port with the VirtualMachine and add an entry to the
445 fae6e5f0 Christos Stavrakakis
    IPAddressLog if the port has a public IPv4 address from a public network.
446 9ba6bb95 Christos Stavrakakis

447 fae6e5f0 Christos Stavrakakis
    """
448 ae2da8a2 Christos Stavrakakis
    if port.machine is not None:
449 ae2da8a2 Christos Stavrakakis
        raise faults.Conflict("Port %s is already in use." % port.id)
450 fae6e5f0 Christos Stavrakakis
    if port.network.public:
451 fae6e5f0 Christos Stavrakakis
        ipv4_address = port.ipv4_address
452 fae6e5f0 Christos Stavrakakis
        if ipv4_address is not None:
453 fae6e5f0 Christos Stavrakakis
            ip_log = IPAddressLog.objects.create(server_id=machine.id,
454 fae6e5f0 Christos Stavrakakis
                                                 network_id=port.network_id,
455 fae6e5f0 Christos Stavrakakis
                                                 address=ipv4_address,
456 fae6e5f0 Christos Stavrakakis
                                                 active=True)
457 fae6e5f0 Christos Stavrakakis
            log.debug("Created IP log entry %s", ip_log)
458 fae6e5f0 Christos Stavrakakis
    port.machine = machine
459 fae6e5f0 Christos Stavrakakis
    port.state = "BUILD"
460 fae6e5f0 Christos Stavrakakis
    port.device_owner = "vm"
461 fae6e5f0 Christos Stavrakakis
    port.save()
462 fae6e5f0 Christos Stavrakakis
    return port
463 9ba6bb95 Christos Stavrakakis
464 9ba6bb95 Christos Stavrakakis
465 fae6e5f0 Christos Stavrakakis
@transaction.commit_on_success
466 fae6e5f0 Christos Stavrakakis
def delete_port(port):
467 fae6e5f0 Christos Stavrakakis
    """Delete a port by removing the NIC card from the instance.
468 9ba6bb95 Christos Stavrakakis

469 fae6e5f0 Christos Stavrakakis
    Send a Job to remove the NIC card from the instance. The port
470 fae6e5f0 Christos Stavrakakis
    will be deleted and the associated IPv4 addressess will be released
471 6e73f499 Christos Stavrakakis
    when the job completes successfully.
472 a52cc1b4 Christos Stavrakakis

473 fae6e5f0 Christos Stavrakakis
    """
474 a52cc1b4 Christos Stavrakakis
475 371ab004 Christos Stavrakakis
    vm = port.machine
476 371ab004 Christos Stavrakakis
    if vm is not None and not vm.deleted:
477 fae6e5f0 Christos Stavrakakis
        vm = disconnect(port.machine, port)
478 fae6e5f0 Christos Stavrakakis
        log.info("Removing port %s, Job: %s", port, vm.task_job_id)
479 fae6e5f0 Christos Stavrakakis
    else:
480 fae6e5f0 Christos Stavrakakis
        backend.remove_nic_ips(port)
481 fae6e5f0 Christos Stavrakakis
        port.delete()
482 fae6e5f0 Christos Stavrakakis
        log.info("Removed port %s", port)
483 fae6e5f0 Christos Stavrakakis
484 fae6e5f0 Christos Stavrakakis
    return port
485 3aecadc8 Christos Stavrakakis
486 3aecadc8 Christos Stavrakakis
487 3aecadc8 Christos Stavrakakis
def create_instance_ports(user_id, networks=None):
488 3aecadc8 Christos Stavrakakis
    # First connect the instance to the networks defined by the admin
489 3aecadc8 Christos Stavrakakis
    forced_ports = create_ports_for_setting(user_id, category="admin")
490 3aecadc8 Christos Stavrakakis
    if networks is None:
491 3aecadc8 Christos Stavrakakis
        # If the user did not asked for any networks, connect instance to
492 3aecadc8 Christos Stavrakakis
        # default networks as defined by the admin
493 3aecadc8 Christos Stavrakakis
        ports = create_ports_for_setting(user_id, category="default")
494 3aecadc8 Christos Stavrakakis
    else:
495 3aecadc8 Christos Stavrakakis
        # Else just connect to the networks that the user defined
496 3aecadc8 Christos Stavrakakis
        ports = create_ports_for_request(user_id, networks)
497 129b94d5 Christos Stavrakakis
    total_ports = forced_ports + ports
498 129b94d5 Christos Stavrakakis
    if len(total_ports) > settings.GANETI_MAX_NICS_PER_INSTANCE:
499 129b94d5 Christos Stavrakakis
        raise faults.BadRequest("Maximum ports per server limit reached")
500 129b94d5 Christos Stavrakakis
    return total_ports
501 3aecadc8 Christos Stavrakakis
502 3aecadc8 Christos Stavrakakis
503 3aecadc8 Christos Stavrakakis
def create_ports_for_setting(user_id, category):
504 3aecadc8 Christos Stavrakakis
    if category == "admin":
505 3aecadc8 Christos Stavrakakis
        network_setting = settings.CYCLADES_FORCED_SERVER_NETWORKS
506 e74a5b4b Christos Stavrakakis
        exception = faults.ServiceUnavailable
507 3aecadc8 Christos Stavrakakis
    elif category == "default":
508 3aecadc8 Christos Stavrakakis
        network_setting = settings.CYCLADES_DEFAULT_SERVER_NETWORKS
509 e74a5b4b Christos Stavrakakis
        exception = faults.Conflict
510 3aecadc8 Christos Stavrakakis
    else:
511 3aecadc8 Christos Stavrakakis
        raise ValueError("Unknown category: %s" % category)
512 3aecadc8 Christos Stavrakakis
513 3aecadc8 Christos Stavrakakis
    ports = []
514 3aecadc8 Christos Stavrakakis
    for network_ids in network_setting:
515 3aecadc8 Christos Stavrakakis
        # Treat even simple network IDs as group of networks with one network
516 3aecadc8 Christos Stavrakakis
        if type(network_ids) not in (list, tuple):
517 3aecadc8 Christos Stavrakakis
            network_ids = [network_ids]
518 3aecadc8 Christos Stavrakakis
519 e74a5b4b Christos Stavrakakis
        error_msgs = []
520 3aecadc8 Christos Stavrakakis
        for network_id in network_ids:
521 c32c74d9 Christos Stavrakakis
            success = False
522 3aecadc8 Christos Stavrakakis
            try:
523 3aecadc8 Christos Stavrakakis
                ports.append(_port_from_setting(user_id, network_id, category))
524 e74a5b4b Christos Stavrakakis
                # Port successfully created in one of the networks. Skip the
525 e74a5b4b Christos Stavrakakis
                # the rest.
526 c32c74d9 Christos Stavrakakis
                success = True
527 3aecadc8 Christos Stavrakakis
                break
528 e74a5b4b Christos Stavrakakis
            except faults.Conflict as e:
529 e74a5b4b Christos Stavrakakis
                if len(network_ids) == 1:
530 e74a5b4b Christos Stavrakakis
                    raise exception(e.message)
531 e74a5b4b Christos Stavrakakis
                else:
532 e74a5b4b Christos Stavrakakis
                    error_msgs.append(e.message)
533 e74a5b4b Christos Stavrakakis
534 c32c74d9 Christos Stavrakakis
        if not success:
535 c32c74d9 Christos Stavrakakis
            if category == "admin":
536 c32c74d9 Christos Stavrakakis
                log.error("Cannot connect server to forced networks '%s': %s",
537 c32c74d9 Christos Stavrakakis
                          network_ids, error_msgs)
538 c32c74d9 Christos Stavrakakis
                raise exception("Cannot connect server to forced server"
539 c32c74d9 Christos Stavrakakis
                                " networks.")
540 c32c74d9 Christos Stavrakakis
            else:
541 c32c74d9 Christos Stavrakakis
                log.debug("Cannot connect server to default networks '%s': %s",
542 c32c74d9 Christos Stavrakakis
                          network_ids, error_msgs)
543 c32c74d9 Christos Stavrakakis
                raise exception("Cannot connect server to default server"
544 c32c74d9 Christos Stavrakakis
                                " networks.")
545 e74a5b4b Christos Stavrakakis
546 3aecadc8 Christos Stavrakakis
    return ports
547 3aecadc8 Christos Stavrakakis
548 3aecadc8 Christos Stavrakakis
549 3aecadc8 Christos Stavrakakis
def _port_from_setting(user_id, network_id, category):
550 3aecadc8 Christos Stavrakakis
    # TODO: Fix this..you need only IPv4 and only IPv6 network
551 3aecadc8 Christos Stavrakakis
    if network_id == "SNF:ANY_PUBLIC_IPV4":
552 3aecadc8 Christos Stavrakakis
        return create_public_ipv4_port(user_id, category=category)
553 3aecadc8 Christos Stavrakakis
    elif network_id == "SNF:ANY_PUBLIC_IPV6":
554 3aecadc8 Christos Stavrakakis
        return create_public_ipv6_port(user_id, category=category)
555 3aecadc8 Christos Stavrakakis
    elif network_id == "SNF:ANY_PUBLIC":
556 3aecadc8 Christos Stavrakakis
        try:
557 3aecadc8 Christos Stavrakakis
            return create_public_ipv4_port(user_id, category=category)
558 e74a5b4b Christos Stavrakakis
        except faults.Conflict as e1:
559 e74a5b4b Christos Stavrakakis
            try:
560 e74a5b4b Christos Stavrakakis
                return create_public_ipv6_port(user_id, category=category)
561 e74a5b4b Christos Stavrakakis
            except faults.Conflict as e2:
562 e74a5b4b Christos Stavrakakis
                log.error("Failed to connect server to a public IPv4 or IPv6"
563 e74a5b4b Christos Stavrakakis
                          " network. IPv4: %s, IPv6: %s", e1, e2)
564 e74a5b4b Christos Stavrakakis
                msg = ("Cannot connect server to a public IPv4 or IPv6"
565 e74a5b4b Christos Stavrakakis
                       " network.")
566 e74a5b4b Christos Stavrakakis
                raise faults.Conflict(msg)
567 3aecadc8 Christos Stavrakakis
    else:  # Case of network ID
568 3aecadc8 Christos Stavrakakis
        if category in ["user", "default"]:
569 3aecadc8 Christos Stavrakakis
            return _port_for_request(user_id, {"uuid": network_id})
570 3aecadc8 Christos Stavrakakis
        elif category == "admin":
571 3aecadc8 Christos Stavrakakis
            network = util.get_network(network_id, user_id, non_deleted=True)
572 3aecadc8 Christos Stavrakakis
            return _create_port(user_id, network)
573 3aecadc8 Christos Stavrakakis
        else:
574 3aecadc8 Christos Stavrakakis
            raise ValueError("Unknown category: %s" % category)
575 3aecadc8 Christos Stavrakakis
576 3aecadc8 Christos Stavrakakis
577 3aecadc8 Christos Stavrakakis
def create_public_ipv4_port(user_id, network=None, address=None,
578 3aecadc8 Christos Stavrakakis
                            category="user"):
579 3aecadc8 Christos Stavrakakis
    """Create a port in a public IPv4 network.
580 3aecadc8 Christos Stavrakakis

581 3aecadc8 Christos Stavrakakis
    Create a port in a public IPv4 network (that may also have an IPv6
582 3aecadc8 Christos Stavrakakis
    subnet). If the category is 'user' or 'default' this will try to use
583 3aecadc8 Christos Stavrakakis
    one of the users floating IPs. If the category is 'admin' will
584 3aecadc8 Christos Stavrakakis
    create a port to the public network (without floating IPs or quotas).
585 3aecadc8 Christos Stavrakakis

586 3aecadc8 Christos Stavrakakis
    """
587 3aecadc8 Christos Stavrakakis
    if category in ["user", "default"]:
588 3aecadc8 Christos Stavrakakis
        if address is None:
589 3aecadc8 Christos Stavrakakis
            ipaddress = ips.get_free_floating_ip(user_id, network)
590 3aecadc8 Christos Stavrakakis
        else:
591 3aecadc8 Christos Stavrakakis
            ipaddress = util.get_floating_ip_by_address(user_id, address,
592 3aecadc8 Christos Stavrakakis
                                                        for_update=True)
593 3aecadc8 Christos Stavrakakis
    elif category == "admin":
594 3aecadc8 Christos Stavrakakis
        if network is None:
595 3aecadc8 Christos Stavrakakis
            ipaddress = ips.allocate_public_ip(user_id)
596 3aecadc8 Christos Stavrakakis
        else:
597 3aecadc8 Christos Stavrakakis
            ipaddress = ips.allocate_ip(network, user_id)
598 3aecadc8 Christos Stavrakakis
    else:
599 3aecadc8 Christos Stavrakakis
        raise ValueError("Unknown category: %s" % category)
600 3aecadc8 Christos Stavrakakis
    if network is None:
601 3aecadc8 Christos Stavrakakis
        network = ipaddress.network
602 3aecadc8 Christos Stavrakakis
    return _create_port(user_id, network, use_ipaddress=ipaddress)
603 3aecadc8 Christos Stavrakakis
604 3aecadc8 Christos Stavrakakis
605 3aecadc8 Christos Stavrakakis
def create_public_ipv6_port(user_id, category=None):
606 3aecadc8 Christos Stavrakakis
    """Create a port in a public IPv6 only network."""
607 3aecadc8 Christos Stavrakakis
    networks = Network.objects.filter(public=True, deleted=False,
608 3aecadc8 Christos Stavrakakis
                                      drained=False, subnets__ipversion=6)\
609 3aecadc8 Christos Stavrakakis
                              .exclude(subnets__ipversion=4)
610 3aecadc8 Christos Stavrakakis
    if networks:
611 3aecadc8 Christos Stavrakakis
        return _create_port(user_id, networks[0])
612 3aecadc8 Christos Stavrakakis
    else:
613 3aecadc8 Christos Stavrakakis
        msg = "No available IPv6 only network!"
614 3aecadc8 Christos Stavrakakis
        log.error(msg)
615 3aecadc8 Christos Stavrakakis
        raise faults.Conflict(msg)
616 3aecadc8 Christos Stavrakakis
617 3aecadc8 Christos Stavrakakis
618 3aecadc8 Christos Stavrakakis
def create_ports_for_request(user_id, networks):
619 3aecadc8 Christos Stavrakakis
    """Create the server ports requested by the user.
620 3aecadc8 Christos Stavrakakis

621 3aecadc8 Christos Stavrakakis
    Create the ports for the new servers as requested in the 'networks'
622 3aecadc8 Christos Stavrakakis
    attribute. The networks attribute contains either a list of network IDs
623 3aecadc8 Christos Stavrakakis
    ('uuid') or a list of ports IDs ('port'). In case of network IDs, the user
624 3aecadc8 Christos Stavrakakis
    can also specify an IPv4 address ('fixed_ip'). In order to connect to a
625 3aecadc8 Christos Stavrakakis
    public network, the 'fixed_ip' attribute must contain the IPv4 address of a
626 3aecadc8 Christos Stavrakakis
    floating IP. If the network is public but the 'fixed_ip' attribute is not
627 3aecadc8 Christos Stavrakakis
    specified, the system will automatically reserve one of the users floating
628 3aecadc8 Christos Stavrakakis
    IPs.
629 3aecadc8 Christos Stavrakakis

630 3aecadc8 Christos Stavrakakis
    """
631 14402edc Christos Stavrakakis
    if not isinstance(networks, list):
632 14402edc Christos Stavrakakis
        raise faults.BadRequest("Malformed request. Invalid 'networks' field")
633 3aecadc8 Christos Stavrakakis
    return [_port_for_request(user_id, network) for network in networks]
634 3aecadc8 Christos Stavrakakis
635 3aecadc8 Christos Stavrakakis
636 3aecadc8 Christos Stavrakakis
def _port_for_request(user_id, network_dict):
637 14402edc Christos Stavrakakis
    if not isinstance(network_dict, dict):
638 14402edc Christos Stavrakakis
        raise faults.BadRequest("Malformed request. Invalid 'networks' field")
639 3aecadc8 Christos Stavrakakis
    port_id = network_dict.get("port")
640 3aecadc8 Christos Stavrakakis
    network_id = network_dict.get("uuid")
641 3aecadc8 Christos Stavrakakis
    if port_id is not None:
642 3aecadc8 Christos Stavrakakis
        return util.get_port(port_id, user_id, for_update=True)
643 3aecadc8 Christos Stavrakakis
    elif network_id is not None:
644 3aecadc8 Christos Stavrakakis
        address = network_dict.get("fixed_ip")
645 3aecadc8 Christos Stavrakakis
        network = util.get_network(network_id, user_id, non_deleted=True)
646 3aecadc8 Christos Stavrakakis
        if network.public:
647 3aecadc8 Christos Stavrakakis
            if network.subnet4 is not None:
648 3aecadc8 Christos Stavrakakis
                if not "fixed_ip" in network_dict:
649 3aecadc8 Christos Stavrakakis
                    return create_public_ipv4_port(user_id, network)
650 3aecadc8 Christos Stavrakakis
                elif address is None:
651 3aecadc8 Christos Stavrakakis
                    msg = "Cannot connect to public network"
652 3aecadc8 Christos Stavrakakis
                    raise faults.BadRequest(msg % network.id)
653 3aecadc8 Christos Stavrakakis
                else:
654 3aecadc8 Christos Stavrakakis
                    return create_public_ipv4_port(user_id, network, address)
655 3aecadc8 Christos Stavrakakis
            else:
656 3aecadc8 Christos Stavrakakis
                raise faults.Forbidden("Cannot connect to IPv6 only public"
657 3aecadc8 Christos Stavrakakis
                                       " network %" % network.id)
658 3aecadc8 Christos Stavrakakis
        else:
659 3aecadc8 Christos Stavrakakis
            return _create_port(user_id, network, address=address)
660 3aecadc8 Christos Stavrakakis
    else:
661 3aecadc8 Christos Stavrakakis
        raise faults.BadRequest("Network 'uuid' or 'port' attribute"
662 3aecadc8 Christos Stavrakakis
                                " is required.")