Revision fae6e5f0 snf-cyclades-app/synnefo/logic/servers.py

b/snf-cyclades-app/synnefo/logic/servers.py
12 12
from synnefo.api import util
13 13
from synnefo.logic import backend
14 14
from synnefo.logic.backend_allocator import BackendAllocator
15
from synnefo.db import pools
15 16
from synnefo.db.models import (NetworkInterface, VirtualMachine,
16
                               VirtualMachineMetadata, IPAddress,
17
                               IPAddressLog)
18
from synnefo.db import query as db_query, pools
19

  
17
                               VirtualMachineMetadata, IPAddressLog)
20 18
from vncauthproxy.client import request_forwarding as request_vnc_forwarding
21 19

  
22 20
log = logging.getLogger(__name__)
......
247 245
        if network_id == "SNF:ANY_PUBLIC":
248 246
            ipaddress = util.allocate_public_ip(userid=userid,
249 247
                                                backend=vm.backend)
250
            nic, ipaddress = create_nic(vm, ipaddress=ipaddress)
248
            nic = _create_port(userid, network=ipaddress.network,
249
                               use_ipaddress=ipaddress)
251 250
        else:
252 251
            try:
253 252
                network = util.get_network(network_id, userid,
......
258 257
                      " network '%s'" % network_id
259 258
                log.error(msg)
260 259
                raise faults.InternalServerError(msg)
261
            nic, ipaddress = create_nic(vm, network=network)
260
            nic = _create_port(userid, network)
262 261
        nics.append(nic)
263 262
    for address in floating_ips:
264 263
        floating_ip = util.get_floating_ip_by_address(vm.userid, address,
265 264
                                                      for_update=True)
266
        nic, ipaddress = create_nic(vm, ipaddress=floating_ip)
265
        nic = _create_port(userid, network=floating_ip.network,
266
                           use_ipaddress=floating_ip)
267 267
        nics.append(nic)
268 268
    for network_id in private_networks:
269 269
        network = util.get_network(network_id, userid, non_deleted=True)
270 270
        if network.public:
271 271
            raise faults.Forbidden("Can not connect to public network")
272
        nic, ipaddress = create_nic(vm, network=network)
272
        nic = _create_port(userid, network)
273 273
        nics.append(nic)
274 274
    for index, nic in enumerate(nics):
275
        associate_port_with_machine(nic, vm)
275 276
        nic.index = index
276 277
        nic.save()
277 278
    return nics
278 279

  
279 280

  
280
def create_nic(vm, network=None, ipaddress=None, address=None, name=None):
281
    """Helper functions for create NIC objects.
282

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

  
287
    """
288
    userid = vm.userid
289

  
290
    if ipaddress is None:
291
        if network.subnets.filter(ipversion=4).exists():
292
            try:
293
                ipaddress = util.allocate_ip(network, userid=userid,
294
                                             address=address)
295
            except pools.ValueNotAvailable:
296
                raise faults.Conflict("Address '%s' is not available." %
297
                                      address)
298

  
299
    if ipaddress is not None and ipaddress.nic is not None:
300
        raise faults.Conflict("IP address '%s' already in use" %
301
                              ipaddress.address)
302

  
303
    if network is None:
304
        network = ipaddress.network
305
    elif network.state != 'ACTIVE':
306
        # TODO: What if is in settings ?
307
        raise faults.BuildInProgress('Network not active yet')
308

  
309
    #device_owner = "router" if vm.router else "vm"
310
    device_owner = "vm"
311
    nic = NetworkInterface.objects.create(machine=vm, network=network,
312
                                          state="BUILD",
313
                                          userid=vm.userid,
314
                                          device_owner=device_owner,
315
                                          name=name)
316
    log.debug("Created NIC %s with IP %s", nic, ipaddress)
317
    if ipaddress is not None:
318
        ipaddress.nic = nic
319
        ipaddress.save()
320

  
321
        if ipaddress.network.public:
322
            ip_log = IPAddressLog.objects.create(
323
                server_id=vm.id,
324
                network_id=ipaddress.network_id,
325
                address=ipaddress.address,
326
                active=True)
327
            log.debug("Created IP log entry %s", ip_log)
328

  
329
    return nic, ipaddress
330

  
331

  
332 281
@server_command("DESTROY")
333 282
def destroy(vm):
334 283
    log.info("Deleting VM %s", vm)
......
395 344
@server_command("CONNECT")
396 345
def connect(vm, network, port=None):
397 346
    if port is None:
398
        nic, ipaddress = create_nic(vm, network)
399
    else:
400
        nic = port
401
        ipaddress = port.ips.all()[0]
347
        port = _create_port(vm.userid, network)
348
    associate_port_with_machine(port, vm)
402 349

  
403
    log.info("Creating NIC %s with IPAddress %s", nic, ipaddress)
350
    log.info("Creating NIC %s with IPv4 Address %s", port, port.ipv4_address)
404 351

  
405
    return backend.connect_to_network(vm, nic)
352
    return backend.connect_to_network(vm, port)
406 353

  
407 354

  
408 355
@server_command("DISCONNECT")
......
467 414
    return console
468 415

  
469 416

  
470
@server_command("CONNECT")
471
def add_floating_ip(vm, address):
472
    # Use for_update, to guarantee that floating IP will only by assigned once
473
    # and that it can not be released will it is being attached!
474
    floating_ip = util.get_floating_ip_by_address(vm.userid, address,
475
                                                  for_update=True)
476
    nic, floating_ip = create_nic(vm, ipaddress=floating_ip)
477
    log.info("Created NIC %s with floating IP %s", nic, floating_ip)
478
    return backend.connect_to_network(vm, nic)
479

  
480

  
481
@server_command("DISCONNECT")
482
def remove_floating_ip(vm, address):
483
    try:
484
        floating_ip = db_query.get_server_floating_ip(server=vm,
485
                                                      address=address,
486
                                                      for_update=True)
487
    except IPAddress.DoesNotExist:
488
        raise faults.BadRequest("Server '%s' has no floating ip with"
489
                                " address '%s'" % (vm, address))
490

  
491
    nic = floating_ip.nic
492
    log.info("Removing NIC %s from VM %s. Floating IP '%s'", str(nic.index),
493
             vm, floating_ip)
494

  
495
    return backend.disconnect_from_network(vm, nic)
496

  
497

  
498 417
def rename(server, new_name):
499 418
    """Rename a VirtualMachine."""
500 419
    old_name = server.name
......
503 422
    log.info("Renamed server '%s' from '%s' to '%s'", server, old_name,
504 423
             new_name)
505 424
    return server
425

  
426

  
427
@transaction.commit_on_success
428
def create_port(*args, **kwargs):
429
    return _create_port(*args, **kwargs)
430

  
431

  
432
def _create_port(userid, network, machine=None, use_ipaddress=None,
433
                 address=None, name="", security_groups=None,
434
                 device_owner=None):
435
    """Create a new port on the specified network.
436

  
437
    Create a new Port(NetworkInterface model) on the specified Network. If
438
    'machine' is specified, the machine will be connected to the network using
439
    this port. If 'use_ipaddress' argument is specified, the port will be
440
    assigned this IPAddress. Otherwise, an IPv4 address from the IPv4 subnet
441
    will be allocated.
442

  
443
    """
444
    if network.state != "ACTIVE":
445
        raise faults.BuildInProgress("Can not create port while network is in"
446
                                     " state %s" % network.state)
447
    ipaddress = None
448
    if use_ipaddress is not None:
449
        # Use an existing IPAddress object.
450
        ipaddress = use_ipaddress
451
        if ipaddress and (ipaddress.network_id != network.id):
452
            msg = "IP Address %s does not belong to network %s"
453
            raise faults.Conflict(msg % (ipaddress.address, network.id))
454
    else:
455
        # If network has IPv4 subnets, try to allocate the address that the
456
        # the user specified or a random one.
457
        if network.subnets.filter(ipversion=4).exists():
458
            try:
459
                ipaddress = util.allocate_ip(network, userid=userid,
460
                                             address=address)
461
            except pools.ValueNotAvailable:
462
                msg = "Address %s is already in use." % address
463
                raise faults.Conflict(msg)
464
        elif address is not None:
465
            raise faults.BadRequest("Address %s is not a valid IP for the"
466
                                    " defined network subnets" % address)
467

  
468
    if ipaddress is not None and ipaddress.nic is not None:
469
        raise faults.Conflict("IP address '%s' is already in use" %
470
                              ipaddress.address)
471

  
472
    port = NetworkInterface.objects.create(network=network,
473
                                           state="DOWN",
474
                                           userid=userid,
475
                                           device_owner=None,
476
                                           name=name)
477

  
478
    # add the security groups if any
479
    if security_groups:
480
        port.security_groups.add(*security_groups)
481

  
482
    if ipaddress is not None:
483
        # Associate IPAddress with the Port
484
        ipaddress.nic = port
485
        ipaddress.save()
486

  
487
    if machine is not None:
488
        # Associate Port(NIC) with VirtualMachine
489
        port = associate_port_with_machine(port, machine)
490
        # Send job to connect port to instance
491
        machine = connect(machine, network, port)
492
        jobID = machine.task_job_id
493
        log.info("Created Port %s with IP %s. Ganeti Job: %s",
494
                 port, ipaddress, jobID)
495
    else:
496
        log.info("Created Port %s with IP %s not attached to any instance",
497
                 port, ipaddress)
498

  
499
    return port
500

  
501

  
502
def associate_port_with_machine(port, machine):
503
    """Associate a Port with a VirtualMachine.
504

  
505
    Associate the port with the VirtualMachine and add an entry to the
506
    IPAddressLog if the port has a public IPv4 address from a public network.
507

  
508
    """
509
    if port.network.public:
510
        ipv4_address = port.ipv4_address
511
        if ipv4_address is not None:
512
            ip_log = IPAddressLog.objects.create(server_id=machine.id,
513
                                                 network_id=port.network_id,
514
                                                 address=ipv4_address,
515
                                                 active=True)
516
            log.debug("Created IP log entry %s", ip_log)
517
    port.machine = machine
518
    port.state = "BUILD"
519
    port.device_owner = "vm"
520
    port.save()
521
    return port
522

  
523

  
524
@transaction.commit_on_success
525
def delete_port(port):
526
    """Delete a port by removing the NIC card from the instance.
527

  
528
    Send a Job to remove the NIC card from the instance. The port
529
    will be deleted and the associated IPv4 addressess will be released
530
    when the job completes successfully.
531

  
532
    """
533

  
534
    if port.machine is not None:
535
        vm = disconnect(port.machine, port)
536
        log.info("Removing port %s, Job: %s", port, vm.task_job_id)
537
    else:
538
        backend.remove_nic_ips(port)
539
        port.delete()
540
        log.info("Removed port %s", port)
541

  
542
    return port

Also available in: Unified diff