Revision 3aecadc8 snf-cyclades-app/synnefo/logic/servers.py

b/snf-cyclades-app/synnefo/logic/servers.py
13 13
from synnefo.logic import backend, ips
14 14
from synnefo.logic.backend_allocator import BackendAllocator
15 15
from synnefo.db.models import (NetworkInterface, VirtualMachine,
16
                               VirtualMachineMetadata, IPAddressLog)
16
                               VirtualMachineMetadata, IPAddressLog, Network)
17 17
from vncauthproxy.client import request_forwarding as request_vnc_forwarding
18 18

  
19 19
log = logging.getLogger(__name__)
......
131 131

  
132 132
@transaction.commit_on_success
133 133
def create(userid, name, password, flavor, image, metadata={},
134
           personality=[], networks=None, floating_ips=None,
135
           use_backend=None):
134
           personality=[], networks=None, use_backend=None):
136 135
    if use_backend is None:
137 136
        # Allocate server to a Ganeti backend
138 137
        use_backend = allocate_new_server(userid, flavor)
139 138

  
140
    if networks is None:
141
        networks = []
142
    if floating_ips is None:
143
        floating_ips = []
139
    # Create the ports for the server
140
    try:
141
        ports = create_instance_ports(userid, networks)
142
    except Exception as e:
143
        raise e
144 144

  
145 145
    # Fix flavor for archipelago
146 146
    disk_template, provider = util.get_flavor_provider(flavor)
......
164 164
                                       operstate="BUILD")
165 165
    log.info("Created entry in DB for VM '%s'", vm)
166 166

  
167
    nics = create_instance_nics(vm, userid, networks, floating_ips)
167
    # Associate the ports with the server
168
    for index, port in enumerate(ports):
169
        associate_port_with_machine(port, vm)
170
        port.index = index
171
        port.save()
168 172

  
169 173
    for key, val in metadata.items():
170 174
        VirtualMachineMetadata.objects.create(
......
173 177
            vm=vm)
174 178

  
175 179
    # Create the server in Ganeti.
176
    vm = create_server(vm, nics, flavor, image, personality, password)
180
    vm = create_server(vm, ports, flavor, image, personality, password)
177 181

  
178 182
    return vm
179 183

  
......
230 234
    return jobID
231 235

  
232 236

  
233
def create_instance_nics(vm, userid, networks=[], floating_ips=[]):
234
    """Create NICs for VirtualMachine.
235

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

  
241
    """
242
    ports = []
243
    for network_id in settings.DEFAULT_INSTANCE_NETWORKS:
244
        if network_id == "SNF:ANY_PUBLIC":
245
            ipaddress = ips.allocate_public_ip(userid=userid,
246
                                               backend=vm.backend)
247
            port = _create_port(userid, network=ipaddress.network,
248
                                use_ipaddress=ipaddress)
249
        else:
250
            try:
251
                network = util.get_network(network_id, userid,
252
                                           non_deleted=True)
253
            except faults.ItemNotFound:
254
                msg = "Invalid configuration. Setting"\
255
                      " 'DEFAULT_INSTANCE_NETWORKS' contains invalid"\
256
                      " network '%s'" % network_id
257
                log.error(msg)
258
                raise faults.InternalServerError(msg)
259
            port = _create_port(userid, network)
260
        ports.append(port)
261

  
262
    for floating_ip_id in floating_ips:
263
        floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id,
264
                                                 for_update=True)
265
        port = _create_port(userid, network=floating_ip.network,
266
                            use_ipaddress=floating_ip)
267
        ports.append(port)
268

  
269
    for net in networks:
270
        port_id = net.get("port")
271
        net_id = net.get("uuid")
272
        if port_id is not None:
273
            port = util.get_port(port_id, userid, for_update=True)
274
            ports.append(port)
275
        elif net_id is not None:
276
            address = net.get("fixed_ip")
277
            network = util.get_network(net_id, userid, non_deleted=True)
278
            if network.public:
279
                if address is None:
280
                    msg = ("Can not connect to public network %s. Specify"
281
                           " 'fixed_ip'" " attribute to connect to a public"
282
                           " network")
283
                    raise faults.BadRequest(msg % network.id)
284
                floating_ip = util.get_floating_ip_by_address(userid,
285
                                                              address,
286
                                                              for_update=True)
287
                port = _create_port(userid, network, use_ipaddress=floating_ip)
288
            else:
289
                port = _create_port(userid, network, address=address)
290
            ports.append(port)
291
        else:
292
            raise faults.BadRequest("Network 'uuid' or 'port' attribute"
293
                                    " is required.")
294

  
295
    for index, port in enumerate(ports):
296
        associate_port_with_machine(port, vm)
297
        port.index = index
298
        port.save()
299
    return ports
300

  
301

  
302 237
@server_command("DESTROY")
303 238
def destroy(vm):
304 239
    log.info("Deleting VM %s", vm)
......
547 482

  
548 483
    Send a Job to remove the NIC card from the instance. The port
549 484
    will be deleted and the associated IPv4 addressess will be released
550
    when the job completes successfully.
485
    when the job completes successfully. Deleting port that is connected to
486
    a public network is allowed only if the port has an associated floating IP
487
    address.
551 488

  
552 489
    """
553 490

  
491
    if port.network.public and not port.ips.filter(floating_ip=True,
492
                                                   deleted=False).exists():
493
        raise faults.Forbidden("Can not disconnect from public network.")
494

  
554 495
    if port.machine is not None:
555 496
        vm = disconnect(port.machine, port)
556 497
        log.info("Removing port %s, Job: %s", port, vm.task_job_id)
......
560 501
        log.info("Removed port %s", port)
561 502

  
562 503
    return port
504

  
505

  
506
def create_instance_ports(user_id, networks=None):
507
    # First connect the instance to the networks defined by the admin
508
    forced_ports = create_ports_for_setting(user_id, category="admin")
509
    if networks is None:
510
        # If the user did not asked for any networks, connect instance to
511
        # default networks as defined by the admin
512
        ports = create_ports_for_setting(user_id, category="default")
513
    else:
514
        # Else just connect to the networks that the user defined
515
        ports = create_ports_for_request(user_id, networks)
516
    return forced_ports + ports
517

  
518

  
519
def create_ports_for_setting(user_id, category):
520
    if category == "admin":
521
        network_setting = settings.CYCLADES_FORCED_SERVER_NETWORKS
522
    elif category == "default":
523
        network_setting = settings.CYCLADES_DEFAULT_SERVER_NETWORKS
524
    else:
525
        raise ValueError("Unknown category: %s" % category)
526

  
527
    ports = []
528
    for network_ids in network_setting:
529
        # Treat even simple network IDs as group of networks with one network
530
        if type(network_ids) not in (list, tuple):
531
            network_ids = [network_ids]
532

  
533
        for network_id in network_ids:
534
            try:
535
                ports.append(_port_from_setting(user_id, network_id, category))
536
                break
537
            except faults.Conflict:
538
                # Try all network IDs in the network group
539
                pass
540

  
541
            # Diffrent exception for each category!
542
            if category == "admin":
543
                exception = faults.ServiceUnavailable
544
            else:
545
                exception = faults.Conflict
546
            raise exception("Cannot connect instance to any of the following"
547
                            " networks %s" % network_ids)
548
    return ports
549

  
550

  
551
def _port_from_setting(user_id, network_id, category):
552
    # TODO: Fix this..you need only IPv4 and only IPv6 network
553
    if network_id == "SNF:ANY_PUBLIC_IPV4":
554
        return create_public_ipv4_port(user_id, category=category)
555
    elif network_id == "SNF:ANY_PUBLIC_IPV6":
556
        return create_public_ipv6_port(user_id, category=category)
557
    elif network_id == "SNF:ANY_PUBLIC":
558
        try:
559
            return create_public_ipv4_port(user_id, category=category)
560
        except faults.Conflict:
561
            return create_public_ipv6_port(user_id, category=category)
562
    else:  # Case of network ID
563
        if category in ["user", "default"]:
564
            return _port_for_request(user_id, {"uuid": network_id})
565
        elif category == "admin":
566
            network = util.get_network(network_id, user_id, non_deleted=True)
567
            return _create_port(user_id, network)
568
        else:
569
            raise ValueError("Unknown category: %s" % category)
570

  
571

  
572
def create_public_ipv4_port(user_id, network=None, address=None,
573
                            category="user"):
574
    """Create a port in a public IPv4 network.
575

  
576
    Create a port in a public IPv4 network (that may also have an IPv6
577
    subnet). If the category is 'user' or 'default' this will try to use
578
    one of the users floating IPs. If the category is 'admin' will
579
    create a port to the public network (without floating IPs or quotas).
580

  
581
    """
582
    if category in ["user", "default"]:
583
        if address is None:
584
            ipaddress = ips.get_free_floating_ip(user_id, network)
585
        else:
586
            ipaddress = util.get_floating_ip_by_address(user_id, address,
587
                                                        for_update=True)
588
    elif category == "admin":
589
        if network is None:
590
            ipaddress = ips.allocate_public_ip(user_id)
591
        else:
592
            ipaddress = ips.allocate_ip(network, user_id)
593
    else:
594
        raise ValueError("Unknown category: %s" % category)
595
    if network is None:
596
        network = ipaddress.network
597
    return _create_port(user_id, network, use_ipaddress=ipaddress)
598

  
599

  
600
def create_public_ipv6_port(user_id, category=None):
601
    """Create a port in a public IPv6 only network."""
602
    networks = Network.objects.filter(public=True, deleted=False,
603
                                      drained=False, subnets__ipversion=6)\
604
                              .exclude(subnets__ipversion=4)
605
    if networks:
606
        return _create_port(user_id, networks[0])
607
    else:
608
        msg = "No available IPv6 only network!"
609
        log.error(msg)
610
        raise faults.Conflict(msg)
611

  
612

  
613
def create_ports_for_request(user_id, networks):
614
    """Create the server ports requested by the user.
615

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

  
625
    """
626
    return [_port_for_request(user_id, network) for network in networks]
627

  
628

  
629
def _port_for_request(user_id, network_dict):
630
    port_id = network_dict.get("port")
631
    network_id = network_dict.get("uuid")
632
    if port_id is not None:
633
        return util.get_port(port_id, user_id, for_update=True)
634
    elif network_id is not None:
635
        address = network_dict.get("fixed_ip")
636
        network = util.get_network(network_id, user_id, non_deleted=True)
637
        if network.public:
638
            if network.subnet4 is not None:
639
                if not "fixed_ip" in network_dict:
640
                    return create_public_ipv4_port(user_id, network)
641
                elif address is None:
642
                    msg = "Cannot connect to public network"
643
                    raise faults.BadRequest(msg % network.id)
644
                else:
645
                    return create_public_ipv4_port(user_id, network, address)
646
            else:
647
                raise faults.Forbidden("Cannot connect to IPv6 only public"
648
                                       " network %" % network.id)
649
        else:
650
            return _create_port(user_id, network, address=address)
651
    else:
652
        raise faults.BadRequest("Network 'uuid' or 'port' attribute"
653
                                " is required.")

Also available in: Unified diff