commit efabd2d425511a1a9a2bc6c3fa241d4d6ebd86b8
Author: Dionysis Grigoropoulos <dgrig@grnet.gr>
Date:   Mon Sep 30 15:45:58 2013 +0300

    cyclades: Neutron API add update_subnet, add tests
    
    Create update_subnet, some minor bugs need fixing
    Add tests for update_subnet and create_subnet
    Fix some bugs in subnets_views

diff --git a/snf-cyclades-app/synnefo/neutron/models_factory.py b/snf-cyclades-app/synnefo/neutron/models_factory.py
index 0d4b9c0..724767f 100644
--- a/snf-cyclades-app/synnefo/neutron/models_factory.py
+++ b/snf-cyclades-app/synnefo/neutron/models_factory.py
@@ -59,4 +59,4 @@ class SubnetFactory(factory.DjangoModelFactory):
     ipversion = 4
     cidr = factory.Sequence(lambda n: '192.168.{0}.0/24'.format(n))
     dhcp = True
-    gateway = factory.Sequence(lambda n: '192.168.{0}.1/24'.format(n))
+    gateway = factory.Sequence(lambda n: '192.168.{0}.1'.format(n))
diff --git a/snf-cyclades-app/synnefo/neutron/subnet_views.py b/snf-cyclades-app/synnefo/neutron/subnet_views.py
index 4bcf582..977b978 100644
--- a/snf-cyclades-app/synnefo/neutron/subnet_views.py
+++ b/snf-cyclades-app/synnefo/neutron/subnet_views.py
@@ -107,24 +107,25 @@ def create_subnet(request):
     if ipversion not in [4, 6]:
         raise api.faults.BadRequest("Malformed IP version type")
 
-    dhcp = subnet.get('enable_dhcp', True)
-    if dhcp not in [True, False]:
-        raise api.faults.BadRequest("Malformed request, enable_dhcp must be "
-                                    "True or False")
-    name = subnet.get('name', None)
-    if len(str(name)) > Subnet.SUBNET_NAME_LENGTH:
-        raise api.faults.BadRequest("Subnet name too long")
+    dhcp = check_dhcp_value(subnet.get('enable_dhcp', True))
+    name = check_name_length(subnet.get('name', None))
 
     # Returns the first available IP in the subnet
     if ipversion == 6:
-        potential_gateway = IPv6Network(cidr).network + 1
+        potential_gateway = str(IPv6Network(cidr).network + 1)
         check_number_of_subnets(network, 6)
     else:
-        potential_gateway = IPv4Network(cidr).network + 1
+        potential_gateway = str(IPv4Network(cidr).network + 1)
         check_number_of_subnets(network, 4)
 
     gateway = subnet.get('gateway_ip', potential_gateway)
-    networks.validate_network_params(cidr, gateway)
+
+    if ipversion == 6:
+        networks.validate_network_params(None, None, cidr, gateway)
+    else:
+        networks.validate_network_params(cidr, gateway)
+
+    check_for_hosts_dns(subnet)
 
     # FIX ME
     try:
@@ -134,7 +135,9 @@ def create_subnet(request):
     except:
         return "Error"
 
-    return HttpResponse(sub, status=200)
+    subnet_dict = subnet_to_dict(sub)
+    data = json.dumps({'subnet': subnet_dict})
+    return HttpResponse(data, status=200)
 
 
 @api.api_method(http_method='GET', user_required=True, logger=log)
@@ -165,11 +168,59 @@ def delete_subnet(request, sub_id):
 @api.api_method(http_method='PUT', user_required=True, logger=log)
 def update_subnet(request, sub_id):
     '''Update info of a subnet'''
+    dictionary = utils.get_request_dict(request)
+    log.info('Update subnet %s', dictionary)
+    user_id = request.user_uniq
 
+    try:
+        subnet = dictionary['subnet']
+    except KeyError:
+        raise api.faults.BadRequest("Malformed request")
 
-# util functions
+    original_subnet = get_subnet_fromdb(sub_id, user_id)
+    original_dict = subnet_to_dict(original_subnet)
+
+    if subnet.get('ip_version', None):
+        raise api.faults.BadRequest("Malformed request, ip_version cannot be "
+                                    "updated")
+    if subnet.get('cidr', None):
+        raise api.faults.BadRequest("Malformed request, cidr cannot be "
+                                    "updated")
+    if subnet.get('allocation_pools', None):
+        raise api.faults.BadRequest("Malformed request, allocation pools "
+                                    "cannot be updated")
+
+    check_for_hosts_dns(subnet)
+    name = subnet.get('name', original_dict['name'])
+    if name:
+        check_name_length(name)
+
+    dhcp = subnet.get('enable_dhcp', original_dict['enable_dhcp'])
+    check_dhcp_value(dhcp)
+
+    gateway = subnet.get('gateway_ip', original_dict['gateway_ip'])
+    #FIX ME, check if IP is in use
+    if original_dict['ip_version'] == 6:
+        networks.validate_network_params(None, None, original_dict['cidr'],
+                                         gateway)
+    else:
+        networks.validate_network_params(original_dict['cidr'], gateway)
 
+    try:
+        original_subnet.gateway = gateway
+        original_subnet.name = name
+        original_subnet.dhcp = dhcp
+        original_subnet.save()
+    except:
+        #Fix me
+        return "Unknown Error"
+
+    subnet_dict = subnet_to_dict(original_subnet)
+    data = json.dumps({'subnet': subnet_dict})
+    return HttpResponse(data, status=200)
 
+
+#Utility functions
 def subnet_to_dict(subnet):
     '''Returns a dictionary containing the info of a subnet'''
     # FIX ME, allocation pools
@@ -183,22 +234,49 @@ def subnet_to_dict(subnet):
 
 
 def check_number_of_subnets(network, version):
-    '''Checks if a user can add a subnet in a network'''
+    '''Check if a user can add a subnet in a network'''
     if network.subnet_set.filter(ipversion=version):
         raise api.faults.BadRequest("Only one subnet of IPv4/IPv6 per "
                                     "network is allowed")
 
 
+def check_dhcp_value(dhcp):
+    '''Check if dhcp value is in acceptable values'''
+    if dhcp not in [True, False]:
+        raise api.faults.BadRequest("Malformed request, enable_dhcp must be "
+                                    "True or False")
+    return dhcp
+
+
+def check_name_length(name):
+    '''Check if the length of a name is within acceptable value'''
+    if len(str(name)) > Subnet.SUBNET_NAME_LENGTH:
+        raise api.faults.BadRequest("Subnet name too long")
+    return name
+
+
+def check_for_hosts_dns(subnet):
+    '''
+    Check if a request contains host_routes or dns_nameservers options
+    Expects the request in a dictionary format
+    '''
+    if subnet.get('host_routes', None):
+        raise api.faults.BadRequest("Setting host routes isn't supported")
+    if subnet.get('dns_nameservers', None):
+        raise api.faults.BadRequest("Setting dns nameservers isn't supported")
+
+
 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)
-        objects = Subnet.objects
         if for_update:
-            objects = objects.select_for_update()
-        return objects.get(id=subnet_id, network__userid=user_id)
+            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.')
diff --git a/snf-cyclades-app/synnefo/neutron/tests/api.py b/snf-cyclades-app/synnefo/neutron/tests/api.py
index 2bc3cfd..cc3cfe7 100644
--- a/snf-cyclades-app/synnefo/neutron/tests/api.py
+++ b/snf-cyclades-app/synnefo/neutron/tests/api.py
@@ -3,10 +3,12 @@ from django.utils import simplejson as json
 from synnefo.cyclades_settings import cyclades_services
 from synnefo.lib.services import get_service_path
 from synnefo.lib import join_urls
+from ipaddr import IPv4Network
 import json
 import synnefo.neutron.models_factory as mf
 import synnefo.db.models_factory as dbmf
 
+
 NEUTRON_URL = get_service_path(cyclades_services, "neutron", "v2.0")
 NETWORKS_URL = join_urls(NEUTRON_URL, "networks/")
 SUBNETS_URL = join_urls(NEUTRON_URL, "subnets/")
@@ -361,7 +363,7 @@ class SubnetTest(BaseAPITest):
         test_net = mf.NetworkFactory()
         test_subnet_ipv4 = mf.SubnetFactory(network=test_net)
         test_subnet_ipv6 = mf.SubnetFactory(network=test_net, ipversion=6,
-                                            cidr='2620: 0:2d0:200::7/32')
+                                            cidr='2620:0:2d0:200::7/32')
         response = self.get(SUBNETS_URL, user=test_net.userid)
         self.assertSuccess(response)
 
@@ -385,18 +387,31 @@ class SubnetTest(BaseAPITest):
         response = self.delete(url)
         self.assertBadRequest(response)
 
-   # def test_create_subnet_success(self):
-   #     '''Test create a subnet successfully'''
-   #     test_net = mf.NetworkFactory()
-   #     request = {
-   #         'subnet': {
-   #             'network_id': test_net.id,
-   #             'cidr': '10.0.3.0/24',
-   #             'ip_version': 4}
-   #     }
-   #     response = self.post(SUBNETS_URL, test_net.userid,
-   #                          json.dumps(request), "json")
-   #     self.assertSuccess(response)
+    def test_create_subnet_success_ipv4(self):
+        '''Test create a subnet successfully'''
+        test_net = mf.NetworkFactory()
+        request = {
+            'subnet': {
+                'network_id': test_net.id,
+                'cidr': '10.0.3.0/24',
+                'ip_version': 4}
+        }
+        response = self.post(SUBNETS_URL, test_net.userid,
+                             json.dumps(request), "json")
+        self.assertSuccess(response)
+
+    def test_create_subnet_success_ipv6(self):
+        '''Test create an IPv6 subnet successfully'''
+        test_net = mf.NetworkFactory()
+        request = {
+            'subnet': {
+                'network_id': test_net.id,
+                'cidr': 'fdc1:4992:1130:fc0b::/64',
+                'ip_version': 6}
+        }
+        response = self.post(SUBNETS_URL, test_net.userid,
+                             json.dumps(request), "json")
+        self.assertSuccess(response)
 
     def test_create_subnet_with_invalid_network_id(self):
         '''Test create a subnet with a network id that doesn't exist'''
@@ -474,3 +489,177 @@ class SubnetTest(BaseAPITest):
         response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
                              "json")
         self.assertBadRequest(response)
+
+    def test_create_subnet_with_dns_nameservers(self):
+        '''Create a subnet with dns nameservers -- raises 400 BadRequest'''
+        test_net = mf.NetworkFactory()
+        request = {
+            'subnet': {
+                'network_id': test_net.id,
+                'cidr': '192.168.3.0/24',
+                'dns_nameservers': '[]'}
+        }
+        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
+                             "json")
+        self.assertBadRequest(response)
+
+    def test_create_subnet_with_host_routes(self):
+        '''Create a subnet with host routes -- raises 400 BadRequest'''
+        test_net = mf.NetworkFactory()
+        request = {
+            'subnet': {
+                'network_id': test_net.id,
+                'cidr': '192.168.3.0/24',
+                'host_routes': '[]'}
+        }
+        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
+                             "json")
+        self.assertBadRequest(response)
+
+    def test_create_subnet_with_same_ipversion(self):
+        '''
+        Create a subnet in a network with another subnet of the same
+        ipversion type
+        '''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'network_id': test_net.id,
+                'cidr': '192.168.3.0/24'}
+        }
+        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
+                             "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_ip_version(self):
+        '''Update the IP version of a subnet, raises 400 BadRequest'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'ip_version': '6'}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_cidr(self):
+        '''Update the cidr of a subnet, raises 400 BadRequest'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'cidr': '192.168.42.0/24'}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_allocation_pools(self):
+        '''Update the allocation pools of a subnet, raises 400 BadRequest'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'allocation_pools': '[]'}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_add_dns(self):
+        '''Update the dns nameservers of a subnet, raises 400 BadRequest'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'dns_nameservers': '[]'}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_add_host_routes(self):
+        '''Update the host routes of a subnet, raises 400 BadRequest'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'host_routes': '[]'}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_with_invalid_dhcp_value(self):
+        '''Update a subnet with an invalid dhcp value'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'enable_dhcp': 'Random'}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_with_invalid_name(self):
+        '''Update a subnet with an invalid name value'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'name': 'a' * 300}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_with_invalid_gateway(self):
+        '''Update a subnet with an invalid gateway value'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'gateway_ip': '192.168.200.0/24'}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertBadRequest(response)
+
+    def test_update_subnet_gateway(self):
+        '''Update the gateway of a subnet'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'gateway_ip': str(IPv4Network(test_sub.gateway).network + 1)}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertSuccess(response)
+
+    def test_update_subnet_name(self):
+        '''Update the name of a subnet'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'name': 'Updated Name'}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertSuccess(response)
+
+    def test_update_subnet_dhcp(self):
+        '''Update the dhcp flag of a subnet'''
+        test_net = mf.NetworkFactory()
+        test_sub = mf.SubnetFactory(network=test_net)
+        request = {
+            'subnet': {
+                'enable_dhcp': False}
+        }
+        url = join_urls(SUBNETS_URL, str(test_sub.id))
+        response = self.put(url, test_net.userid, json.dumps(request), "json")
+        self.assertSuccess(response)
