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