Commit a0ad2412 authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

cyclades: Support IPv6 only networks

Make Cyclades support IPv6 only networks. Such networks will have
the 'subnet' attribute set to None, in the same way that IPv4 only
networks have the 'subnet6' attribute set to None.

Because currently Ganeti does not support IPv6 only networks, we create the
corresponding Ganeti network with a dummy IPv4 subnet(10.0.0.0/24) that will be
never used, since Cyclades will connect instances to IPv6 networks with
'address' attribute set to None.
parent 2d5162ce
......@@ -149,6 +149,9 @@ class Command(BaseCommand):
dry_run = options["dry_run"]
name = options['name']
subnet = options['subnet']
gateway = options['gateway']
subnet6 = options['subnet6']
gateway6 = options['gateway6']
backend_id = options['backend_id']
public = options['public']
flavor = options['flavor']
......@@ -160,11 +163,17 @@ class Command(BaseCommand):
floating_ip_pool = parse_bool(options["floating_ip_pool"])
if not name:
raise CommandError("Name is required")
if not subnet:
raise CommandError("Subnet is required")
raise CommandError("name is required")
if not flavor:
raise CommandError("Flavor is required")
raise CommandError("flavor is required")
if (subnet is None) and (subnet6 is None):
raise CommandError("subnet or subnet6 is required")
if subnet is None and gateway is not None:
raise CommandError("Can not use gateway without subnet")
if subnet6 is None and gateway6 is not None:
raise CommandError("Can not use gateway6 without subnet6")
if public and not (backend_id or floating_ip_pool):
raise CommandError("backend-id is required")
if not userid and not public:
......
......@@ -231,27 +231,30 @@ def get_floating_ip(user_id, ipv4, for_update=False):
raise faults.ItemNotFound("Floating IP does not exist.")
def validate_network_params(subnet, gateway=None, subnet6=None, gateway6=None):
try:
# Use strict option to not all subnets with host bits set
network = ipaddr.IPv4Network(subnet, strict=True)
except ValueError:
raise faults.BadRequest("Invalid network IPv4 subnet")
# Check that network size is allowed!
if not validate_network_size(network.prefixlen):
raise faults.OverLimit(message="Unsupported network size",
details="Network mask must be in range"
" (%s, 29]" % MAX_CIDR_BLOCK)
def validate_network_params(subnet=None, gateway=None, subnet6=None,
gateway6=None):
if (subnet is None) and (subnet6 is None):
raise faults.BadRequest("subnet or subnet6 is required")
# Check that gateway belongs to network
if gateway:
if subnet:
try:
gateway = ipaddr.IPv4Address(gateway)
# Use strict option to not all subnets with host bits set
network = ipaddr.IPv4Network(subnet, strict=True)
except ValueError:
raise faults.BadRequest("Invalid network IPv4 gateway")
if not gateway in network:
raise faults.BadRequest("Invalid network IPv4 gateway")
raise faults.BadRequest("Invalid network IPv4 subnet")
# Check that network size is allowed!
if not validate_network_size(network.prefixlen):
raise faults.OverLimit(message="Unsupported network size",
details="Network mask must be in range"
" (%s, 29]" % MAX_CIDR_BLOCK)
if gateway: # Check that gateway belongs to network
try:
gateway = ipaddr.IPv4Address(gateway)
except ValueError:
raise faults.BadRequest("Invalid network IPv4 gateway")
if not gateway in network:
raise faults.BadRequest("Invalid network IPv4 gateway")
if subnet6:
try:
......@@ -292,6 +295,7 @@ def backend_public_networks(backend):
"""
bnets = BackendNetwork.objects.filter(backend=backend,
network__public=True,
network__subnet__isnull=False,
network__deleted=False,
network__drained=False)
return [b.network for b in bnets]
......
......@@ -495,7 +495,9 @@ class Network(models.Model):
name = models.CharField('Network Name', max_length=128)
userid = models.CharField('User ID of the owner', max_length=128,
null=True, db_index=True)
subnet = models.CharField('Subnet', max_length=32, default='10.0.0.0/24')
# subnet will be null for IPv6 only networks
subnet = models.CharField('Subnet', max_length=32, null=True)
# subnet6 will be null for IPv4 only networks
subnet6 = models.CharField('IPv6 Subnet', max_length=64, null=True)
gateway = models.CharField('Gateway', max_length=32, null=True)
gateway6 = models.CharField('IPv6 Gateway', max_length=64, null=True)
......
......@@ -141,6 +141,7 @@ class NetworkFactory(factory.DjangoModelFactory):
userid = factory.Sequence(user_seq())
subnet = factory.Sequence(lambda n: '192.168.{0}.0/24'.format(n))
gateway = factory.LazyAttribute(lambda a: a.subnet[:-4] + '1')
subnet6 = "2001:648:2ffc:1112::/64"
dhcp = False
flavor = factory.Sequence(round_seq(models.Network.FLAVORS.keys()))
mode = factory.LazyAttribute(lambda a:
......@@ -154,6 +155,11 @@ class NetworkFactory(factory.DjangoModelFactory):
state = factory.Sequence(round_seq_first(models.Network.OPER_STATES))
class IPv6NetworkFactory(NetworkFactory):
subnet = None
gateway = None
class DeletedNetwork(NetworkFactory):
deleted = True
......@@ -172,8 +178,8 @@ class NetworkInterfaceFactory(factory.DjangoModelFactory):
machine = factory.SubFactory(VirtualMachineFactory)
network = factory.SubFactory(NetworkFactory)
index = factory.Sequence(lambda x: x, type=int)
mac = factory.Sequence(lambda n:
'aa:{0}{0}:{0}{0}:aa:{0}{0}:{0}{0}'.format(hex(int(n) % 15)[2:3]))
mac = factory.Sequence(lambda n: 'aa:{0}{0}:{0}{0}:aa:{0}{0}:{0}{0}'
.format(hex(int(n) % 15)[2:3]))
ipv4 = factory.LazyAttributeSequence(lambda a, n: a.network.subnet[:-4] +
'{0}'.format(int(n) + 2))
state = "ACTIVE"
......
......@@ -652,6 +652,14 @@ def _create_network(network, backend):
else:
conflicts_check = False
# Use a dummy network subnet for IPv6 only networks. Currently Ganeti does
# not support IPv6 only networks. To bypass this limitation, we create the
# network with a dummy network subnet, and make Cyclades connect instances
# to such networks, with address=None.
subnet = network.subnet
if subnet is None:
subnet = "10.0.0.0/24"
try:
bn = BackendNetwork.objects.get(network=network, backend=backend)
mac_prefix = bn.mac_prefix
......@@ -661,7 +669,7 @@ def _create_network(network, backend):
with pooled_rapi_client(backend) as client:
return client.CreateNetwork(network_name=network.backend_id,
network=network.subnet,
network=subnet,
network6=network.subnet6,
gateway=network.gateway,
gateway6=network.gateway6,
......
# Copyright 2012 GRNET S.A. All rights reserved.
# Copyright 2012-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -31,8 +31,9 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from synnefo.db.models import Backend, IPPoolTable
from synnefo.db.models import Backend
from synnefo.webproject.management.commands import ListCommand
from synnefo.api import util
class Command(ListCommand):
......@@ -51,17 +52,10 @@ class Command(ListCommand):
def get_ips(backend):
free_ips = 0
total_ips = 0
for bnet in backend.networks.filter(deleted=False,
network__drained=False,
network__public=True,
network__deleted=False):
network = bnet.network
try:
pool = IPPoolTable.objects.get(id=network.pool_id).pool
free_ips += pool.count_available()
total_ips += pool.pool_size
except IPPoolTable.DoesNotExist:
pass
for network in util.backend_public_networks(backend):
pool = network.get_pool(with_lock=False)
free_ips += pool.count_available()
total_ips += pool.pool_size
return "%s/%s" % (free_ips, total_ips)
FIELDS = {
......
......@@ -125,8 +125,8 @@ def server_command(action):
@transaction.commit_manually
def create(userid, name, password, flavor, image, metadata={},
personality=[], network=None, private_networks=None,
floating_ips=None, use_backend=None):
personality=[], private_networks=None, floating_ips=None,
use_backend=None):
if use_backend is None:
# Allocate backend to host the server. Commit after allocation to
# release the locks hold by the backend allocator.
......@@ -250,7 +250,7 @@ def create_instance_nics(vm, userid, private_networks=[], floating_ips=[]):
" network '%s'" % network_id
log.error(msg)
raise Exception(msg)
if network.dhcp:
if network.subnet is not None and network.dhcp:
address = util.get_network_free_address(network)
attachments.append((network, address))
for address in floating_ips:
......@@ -347,7 +347,7 @@ def connect(vm, network):
raise faults.BuildInProgress('Network not active yet')
address = None
if network.dhcp:
if network.subnet is not None and network.dhcp:
# Get a free IP from the address pool.
address = util.get_network_free_address(network)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment