Commit 490a9d07 authored by Dionysis Grigoropoulos's avatar Dionysis Grigoropoulos
Browse files

cyclades: Various refactors in subnets

* Remove unused functions and imports from logic/subnets.py
* Remove --network-id option from snf-manage subnet-modify and make
it an argument
* Fix --ip-version, --dhcp and --gateway options in snf-manage
subnet-create
* Move some code from logic/subnets.py to api/subnets.py
* Refactor the way we handle the gateway IP
* s/slac/slaac/g
parent df4df67f
......@@ -35,6 +35,7 @@ from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from synnefo.management import common
from snf_django.management.utils import parse_bool
from synnefo.logic import subnets
......@@ -52,8 +53,7 @@ class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option("--network-id", dest="network_id",
help="Specify the Network to attach the subnet. To get the"
" networks of a user, use snf-manage network-list"
" --user ID."),
" networks of a user, use snf-manage network-list"),
make_option("--cidr", dest="cidr",
help="The CIDR of the subnet, e.g., 192.168.42.0/24"),
make_option("--allocation-pools", dest="allocation_pools",
......@@ -63,18 +63,18 @@ class Command(BaseCommand):
"[192.168.42.220,129.168.42.240]]'"),
make_option("--name", dest="name",
help="An arbitrary string for naming the subnet."),
make_option("--ip-version", dest="ipversion",
help="IP version of the CIDR. The only acceptable value"
" is 4 or 6. The value must also be in sync with the"
" CIDR. Default value: 4"),
make_option("--ip-version", dest="ipversion", choices=["4", "6"],
metavar="4|6",
help="IP version of the CIDR. The value must be in sync"
" with the CIDR. Default value: 4"),
make_option("--gateway", dest="gateway",
help="An IP to use as a gateway for the subnet."
" The IP must be inside the CIDR range and cannot be the"
" subnet or broadcast IP. If no value is specified, the"
" first available IP of the subnet will be used."),
make_option("--no-dhcp", action="store_true", dest="dhcp",
default=False,
help="True/False value for DHCP/SLAAC. True by default."),
" subnet or broadcast IP. If no value is specified, a"
" gateway will not be set."),
make_option("--dhcp", dest="dhcp", default="True",
choices=["True", "False"], metavar="True|False",
help="Value for DHCP/SLAAC. True by default."),
make_option("--dns", dest="dns",
help="DNS nameservers to be used by the VMs in the subnet."
" For the time being, this option isn't supported."),
......@@ -98,22 +98,12 @@ class Command(BaseCommand):
raise CommandError("cidr is mandatory")
user_id = common.get_network(network_id).userid
name = options["name"]
name = options["name"] or ""
allocation_pools = options["allocation_pools"]
ipversion = options["ipversion"]
if not ipversion:
ipversion = 4
else:
try:
ipversion = int(ipversion)
except ValueError:
raise CommandError("ip-version must be 4 or 6")
ipversion = options["ipversion"] or 4
ipversion = int(ipversion)
gateway = options["gateway"]
if not gateway:
gateway = ""
dhcp = options["dhcp"]
dhcp = False if dhcp else True
dhcp = parse_bool(options["dhcp"])
dns = options["dns"]
host_routes = options["host_routes"]
......@@ -124,7 +114,7 @@ class Command(BaseCommand):
gateway=gateway,
ipversion=ipversion,
dhcp=dhcp,
slac=dhcp,
slaac=dhcp,
dns_nameservers=dns,
host_routes=host_routes,
user_id=user_id)
......@@ -49,25 +49,22 @@ class Command(BaseCommand):
help = "Update a Subnet." + HELP_MSG
option_list = BaseCommand.option_list + (
make_option("--subnet-id", dest="subnet_id",
help="Specify the Subnet ID to update. To get all"
" subnets, use snf-manage subnet-list."),
make_option("--name", dest="name",
help="The new subnet name."),
)
@common.convert_api_faults
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
if len(args) != 1:
raise CommandError("Command accepts only the subnet ID as an"
" argument. Use snf-manage subnet-modify --help"
" for more info.")
subnet_id = options["subnet_id"]
subnet_id = args[0]
name = options["name"]
if not subnet_id:
raise CommandError("subnet-id is mandatory")
if not name:
raise CommandError("name is mandatory")
raise CommandError("--name is mandatory")
subnet = common.get_subnet(subnet_id)
user_id = common.get_network(subnet.network.id).userid
......
......@@ -33,17 +33,16 @@
from logging import getLogger
from snf_django.lib import api
from snf_django.lib.api import faults
from django.conf.urls import patterns
from django.http import HttpResponse
from django.utils import simplejson as json
from snf_django.lib.api import utils
from synnefo.db.models import Subnet, Network, IPPoolTable
from synnefo.logic import networks, subnets
from synnefo.db.models import Subnet
from synnefo.logic import subnets
from ipaddr import IPv4Network, IPv6Network, IPv4Address, IPAddress, IPNetwork
import ipaddr
log = getLogger(__name__)
......@@ -109,11 +108,30 @@ def create_subnet(request):
name = subnet.get('name', None)
ipversion = subnet.get('ip_version', 4)
# If no gateway is specified, send an empty string, because None is used
# if the user wants no gateway at all
gateway = subnet.get('gateway_ip', "")
try:
cidr_ip = ipaddr.IPNetwork(cidr)
except ValueError:
raise api.faults.BadRequest("Malformed CIDR")
potential_gateway = str(ipaddr.IPNetwork(cidr).network + 1)
if gateway is "":
gateway = potential_gateway
dhcp = subnet.get('enable_dhcp', True)
slac = subnet.get('enable_slac', None)
slaac = subnet.get('enable_slaac', None)
if ipversion == 6:
if slaac is not None:
dhcp = check_boolean_value(slaac, "enable_slaac")
else:
dhcp = check_boolean_value(dhcp, "dhcp")
else:
dhcp = check_boolean_value(dhcp, "dhcp")
dns = subnet.get('dns_nameservers', None)
hosts = subnet.get('host_routes', None)
......@@ -123,7 +141,7 @@ def create_subnet(request):
ipversion=ipversion,
gateway=gateway,
dhcp=dhcp,
slac=slac,
slaac=slaac,
dns_nameservers=dns,
allocation_pools=allocation_pools,
host_routes=hosts,
......@@ -192,7 +210,7 @@ def subnet_to_dict(subnet):
if allocation_pools:
for pool in allocation_pools:
cidr = IPNetwork(pool.base)
cidr = ipaddr.IPNetwork(pool.base)
start = str(cidr.network + pool.offset)
end = str(cidr.network + pool.offset + pool.size - 1)
pools.append({"start": start, "end": end})
......@@ -211,7 +229,7 @@ def subnet_to_dict(subnet):
'allocation_pools': pools if pools is not None else []})
if subnet.ipversion == 6:
dictionary['enable_slac'] = subnet.dhcp
dictionary['enable_slaac'] = subnet.dhcp
return dictionary
......@@ -225,7 +243,7 @@ def string_to_ipaddr(pools):
and sort the output
"""
pool_list = [(map(lambda ip_str: IPAddress(ip_str), pool))
pool_list = [(map(lambda ip_str: ipaddr.IPAddress(ip_str), pool))
for pool in pools]
pool_list.sort()
return pool_list
......@@ -274,3 +292,11 @@ def parse_ip_pools(pools):
parse = [pool["start"], pool["end"]]
pool_list.append(parse)
return pool_list
def check_boolean_value(value, key):
"""Check if dhcp value is in acceptable values"""
if value not in [True, False]:
raise api.faults.BadRequest("Malformed request, %s must "
"be True or False" % key)
return value
......@@ -97,29 +97,29 @@ class SubnetTest(BaseAPITest):
resp['allocation_pools'])
self.assertEqual(True, resp['enable_dhcp'])
def test_create_subnet_success_ipv4_with_slac(self):
"""Test create an IPv4 subnet, with a slac that will be ingored"""
def test_create_subnet_success_ipv4_with_slaac(self):
"""Test create an IPv4 subnet, with a slaac that will be ingored"""
test_net = mf.NetworkFactory()
request = {
'subnet': {
'network_id': test_net.id,
'cidr': '10.0.3.0/24',
'ip_version': 4,
'enable_slac': False}
'enable_slaac': False}
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess(response)
def test_create_subnet_success_ipv6_with_slac(self):
"""Test create a subnet with ipv6 and slac"""
def test_create_subnet_success_ipv6_with_slaac(self):
"""Test create a subnet with ipv6 and slaac"""
test_net = mf.NetworkFactory()
request = {
'subnet': {
'network_id': test_net.id,
'cidr': 'fdc1:4992:1130:fc0b::/64',
'ip_version': 6,
'enable_slac': False}
'enable_slaac': False}
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
......@@ -127,17 +127,17 @@ class SubnetTest(BaseAPITest):
resp = json.loads(response.content)['subnet']
self.assertEqual("fdc1:4992:1130:fc0b::1", resp['gateway_ip'])
self.assertEqual([], resp['allocation_pools'])
self.assertEqual(False, resp['enable_slac'])
self.assertEqual(False, resp['enable_slaac'])
def test_create_subnet_with_malformed_slac(self):
"""Test create a subnet with ipv6 and a malformed slac"""
def test_create_subnet_with_malformed_slaac(self):
"""Test create a subnet with ipv6 and a malformed slaac"""
test_net = mf.NetworkFactory()
request = {
'subnet': {
'network_id': test_net.id,
'cidr': 'fdc1:4992:1130:fc0b::/64',
'ip_version': 6,
'enable_slac': 'Random'}
'enable_slaac': 'Random'}
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
......
......@@ -69,7 +69,7 @@ def create_subnet(*args, **kwargs):
def _create_subnet(network_id, user_id, cidr, name, ipversion=4, gateway=None,
dhcp=True, slac=True, dns_nameservers=None,
dhcp=True, slaac=True, dns_nameservers=None,
allocation_pools=None, host_routes=None):
"""Create a subnet
......@@ -79,7 +79,7 @@ def _create_subnet(network_id, user_id, cidr, name, ipversion=4, gateway=None,
try:
network = Network.objects.get(id=network_id)
except Network.DoesNotExist:
raise api.faults.ItemNotFound("No networks found with that id")
raise api.faults.ItemNotFound("No network found with that id")
if user_id != network.userid:
raise api.faults.Unauthorized("Unauthorized operation")
......@@ -94,30 +94,20 @@ def _create_subnet(network_id, user_id, cidr, name, ipversion=4, gateway=None,
cidr_ip = ipaddr.IPNetwork(cidr)
except ValueError:
raise api.faults.BadRequest("Malformed CIDR")
potential_gateway = str(ipaddr.IPNetwork(cidr).network + 1)
if gateway is "":
gateway = potential_gateway
if ipversion == 6:
validate_subnet_params(None, None, cidr, gateway)
if slac is not None:
dhcp = check_boolean_value(slac, "enable_slac")
else:
dhcp = check_boolean_value(dhcp, "dhcp")
else:
validate_subnet_params(cidr, gateway)
dhcp = check_boolean_value(dhcp, "dhcp")
name = check_name_length(name)
gateway_ip = ipaddr.IPAddress(gateway)
sub = Subnet.objects.create(name=name, network=network, cidr=cidr,
ipversion=ipversion, gateway=gateway,
dhcp=dhcp, host_routes=host_routes,
dns_nameservers=dns_nameservers)
gateway_ip = ipaddr.IPAddress(gateway) if gateway else None
if allocation_pools is not None:
# If the user specified IP allocation pools, validate them and use them
if ipversion == 6:
......@@ -126,13 +116,16 @@ def _create_subnet(network_id, user_id, cidr, name, ipversion=4, gateway=None,
if allocation_pools is None and ipversion == 4:
# Check if the gateway is the first IP of the subnet, in this case
# create a single ip pool
if int(gateway_ip) - int(cidr_ip) == 1:
allocation_pools = [[gateway_ip + 1, cidr_ip.broadcast - 1]]
if gateway_ip:
if int(gateway_ip) - int(cidr_ip) == 1:
allocation_pools = [[gateway_ip + 1, cidr_ip.broadcast - 1]]
else:
# If the gateway isn't the first available ip, create two
# different ip pools adjacent to said ip
allocation_pools = (([cidr_ip.network + 1, gateway_ip - 1]),
([gateway_ip + 1, cidr_ip.broadcast - 1]))
else:
# If the gateway isn't the first available ip, create two different
# ip pools adjacent to said ip
allocation_pools = (([cidr_ip.network + 1, gateway_ip - 1]),
([gateway_ip + 1, cidr_ip.broadcast - 1]))
allocation_pools = [[cidr_ip.network + 1, cidr_ip.broadcast - 1]]
if allocation_pools:
create_ip_pools(allocation_pools, cidr_ip, sub)
......@@ -197,13 +190,6 @@ def create_ip_pools(pools, cidr, subnet):
return ip_pools
def check_empty_lists(value):
"""Check if value is Null/None, in which case we return an empty list"""
if value is None:
return []
return value
def check_number_of_subnets(network, version):
"""Check if a user can add a subnet in a network"""
if network.subnets.filter(ipversion=version):
......@@ -211,14 +197,6 @@ def check_number_of_subnets(network, version):
"network is allowed")
def check_boolean_value(value, key):
"""Check if dhcp value is in acceptable values"""
if value not in [True, False]:
raise api.faults.BadRequest("Malformed request, %s must "
"be True or False" % key)
return value
def check_name_length(name):
"""Check if the length of a name is within acceptable value"""
if len(str(name)) > Subnet.SUBNET_NAME_LENGTH:
......@@ -226,22 +204,6 @@ def check_name_length(name):
return name
def get_subnet_fromdb(subnet_id, user_id, for_update=False):
"""Return a Subnet instance or raise ItemNotFound.
This is the same as util.get_network
"""
try:
subnet_id = int(subnet_id)
if for_update:
return Subnet.objects.select_for_update().get(id=subnet_id,
network__userid=
user_id)
return Subnet.objects.get(id=subnet_id, network__userid=user_id)
except (ValueError, Subnet.DoesNotExist):
raise api.faults.ItemNotFound('Subnet not found')
def validate_subpools(pool_list, cidr, gateway):
"""Validate IP Pools
......@@ -263,8 +225,9 @@ def validate_subpools(pool_list, cidr, gateway):
if start > end:
raise api.faults.Conflict("Invalid IP pool range")
# Raise BadRequest if gateway is inside the pool range
if not (gateway < start or gateway > end):
raise api.faults.Conflict("Gateway cannot be in pool range")
if gateway:
if not (gateway < start or gateway > end):
raise api.faults.Conflict("Gateway cannot be in pool range")
# Check if there is a conflict between the IP Poll ranges
end = cidr.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