Revision 0dae1b9f

b/snf-cyclades-app/synnefo/neutron/models.py
216 216
class Subnet(models.Model):
217 217
    SUBNET_NAME_LENGTH = 128
218 218

  
219
    subnet_id = models.CharField('ID of the subnet', max_length=128,
220
                                 null=True, db_index=True, primary_key=True)
221 219
    network = models.ForeignKey('Network')
222

  
223
    name = models.CharField('Network Name', max_length=SUBNET_NAME_LENGTH)
220
    name = models.CharField('Network Name', max_length=SUBNET_NAME_LENGTH,
221
                            null=True)
224 222
    ipversion = models.IntegerField('IP Version', default=4)
225 223
    cidr = models.CharField('Subnet', max_length=32, null=True)
226 224
    gateway = models.CharField('Gateway', max_length=32, null=True)
......
228 226

  
229 227
    # Synnefo related fields
230 228
    # subnet6 will be null for IPv4 only networks
231
    subnet6 = models.CharField('IPv6 Subnet', max_length=64, null=True)
232
    gateway6 = models.CharField('IPv6 Gateway', max_length=64, null=True)
233 229
    #pool = models.OneToOneField('IPPoolTable', related_name='network',
234 230
    #                            default=lambda: IPPoolTable.objects.create(
235 231
    #                                                        available_map='',
......
237 233
    #                                                        size=0),
238 234
    #                           null=True)
239 235

  
236
    def __unicode__(self):
237
        return "<Subnet %s>" % str(self.id)
238

  
240 239

  
241 240
class NetworkInterface(models.Model):
242 241
    STATES = (
b/snf-cyclades-app/synnefo/neutron/models_factory.py
1 1
import factory
2 2
import models
3
from random import choice
4
from string import letters, digits
3 5
from synnefo.db.models_factory import VirtualMachineFactory
4 6

  
5 7

  
......
21 23
    return lambda n: x[int(n) % size][0]
22 24

  
23 25

  
26
def random_string(x):
27
    '''Returns a random string of length x'''
28
    return ''.join([choice(digits + letters) for i in range(x)])
29

  
30

  
24 31
class NetworkFactory(factory.DjangoModelFactory):
25 32
    FACTORY_FOR = models.Network
26 33

  
......
43 50

  
44 51
    machine = factory.SubFactory(VirtualMachineFactory)
45 52
    network = factory.SubFactory(NetworkFactory)
53

  
54

  
55
class SubnetFactory(factory.DjangoModelFactory):
56
    FACTORY_FOR = models.Subnet
57

  
58
    name = factory.LazyAttribute(lambda self: random_string(30))
59
    ipversion = 4
60
    cidr = factory.Sequence(lambda n: '192.168.{0}.0/24'.format(n))
61
    dhcp = True
62
    gateway = factory.Sequence(lambda n: '192.168.{0}.1/24'.format(n))
b/snf-cyclades-app/synnefo/neutron/network_views.py
236 236
    d['status'] = network.state
237 237
    d['public'] = network.public
238 238
    d['admin_state_up'] = "true"
239
    subnet_cidr = [s.subnet_id for s in network.subnet_set.all()]
239
    subnet_cidr = [s.id for s in network.subnet_set.all()]
240 240
    d['subnets'] = subnet_cidr
241 241
    return d
242 242

  
b/snf-cyclades-app/synnefo/neutron/subnet_views.py
39 39
from django.utils import simplejson as json
40 40

  
41 41
from snf_django.lib.api import utils
42
from models import Subnet
42
from models import Subnet, Network
43 43
from synnefo.logic import networks
44 44

  
45
import ipaddr
45
from ipaddr import IPv4Network, IPv6Network
46 46

  
47 47
log = getLogger(__name__)
48 48

  
......
56 56
        return api.api_method_not_allowed(request)
57 57

  
58 58

  
59
def subnet_demux(request, offset):
59
def subnet_demux(request, sub_id):
60 60
    if request.method == 'GET':
61
        return get_subnet(request, offset)
61
        return get_subnet(request, sub_id)
62 62
    elif request.method == 'DELETE':
63
        return delete_subnet(request, offset)
63
        return delete_subnet(request, sub_id)
64 64
    elif request.method == 'PUT':
65
        return update_subnet(request, offset)
65
        return update_subnet(request, sub_id)
66 66
    else:
67 67
        return api.api_method_not_allowed(request)
68 68

  
......
73 73
    log.debug('list_subnets')
74 74

  
75 75
    user_subnets = Subnet.objects.filter(network__userid=request.user_uniq)
76
    subnets_dict = [subnet_to_dict(user_subnets)
77
                    for net in user_subnets.order_by('name')]
76
    subnets_dict = [subnet_to_dict(sub)
77
                    for sub in user_subnets.order_by('id')]
78 78
    data = json.dumps({'subnets': subnets_dict})
79 79

  
80 80
    return HttpResponse(data, status=200)
......
84 84
def create_subnet(request):
85 85
    '''Create a subnet'''
86 86

  
87
    dic = utils.get_request_dict(request)
88
    log.info('create subnet %s', dic)
87
    dictionary = utils.get_request_dict(request)
88
    log.info('create subnet %s', dictionary)
89 89
    user_id = request.user_uniq
90 90

  
91 91
    try:
92
        subnet = dic['subnet']
92
        subnet = dictionary['subnet']
93 93
        network_id = subnet['network_id']
94 94
        cidr = subnet['cidr']
95 95
    except KeyError:
96 96
        raise api.faults.BadRequest("Malformed request")
97 97

  
98
    try:
99
        network = Network.objects.get(id=network_id)
100
    except Network.DoesNotExist:
101
        raise api.faults.ItemNotFound("No networks found with that id")
102

  
103
    if user_id != network.userid:
104
        raise api.faults.Unauthorized("Unauthorized operation")
105

  
98 106
    ipversion = subnet.get('ip_version', 4)
99 107
    if ipversion not in [4, 6]:
100 108
        raise api.faults.BadRequest("Malformed IP version type")
101 109

  
102 110
    dhcp = subnet.get('enable_dhcp', True)
103 111
    if dhcp not in [True, False]:
104
        raise api.faults.BadRequest("Malformed request, enable_dhcp must be"
105
                                    " True or False")
112
        raise api.faults.BadRequest("Malformed request, enable_dhcp must be "
113
                                    "True or False")
106 114
    name = subnet.get('name', None)
107 115
    if len(str(name)) > Subnet.SUBNET_NAME_LENGTH:
108 116
        raise api.faults.BadRequest("Subnet name too long")
109 117

  
110
    # FIX ME, SNF:gateway6 vs gateway6
111
    gateway6 = subnet.get('SNF:gateway6', None)
112
    subnet6 = subnet.get('SNF:subnet6', None)
113

  
114 118
    # Returns the first available IP in the subnet
115
    potential_gateway = ipaddr.IPv4Network(cidr).network + 1
116
    gateway = subnet.get('gateway_ip', potential_gateway)
119
    if ipversion == 6:
120
        potential_gateway = IPv6Network(cidr).network + 1
121
        check_number_of_subnets(network, 6)
122
    else:
123
        potential_gateway = IPv4Network(cidr).network + 1
124
        check_number_of_subnets(network, 4)
117 125

  
118
    networks.validate_network_params(cidr, gateway, subnet6, gateway6)
126
    gateway = subnet.get('gateway_ip', potential_gateway)
127
    networks.validate_network_params(cidr, gateway)
119 128

  
120 129
    # FIX ME
121 130
    try:
122
        sub = Subnet.objects.create(name=name, network_id=network_id,
123
                                    cidr=cidr, ipversion=ipversion,
124
                                    gateway=gateway, gateway6=gateway6,
125
                                    subnet6=subnet6)
131
        sub = Subnet.objects.create(name=name, network=network, cidr=cidr,
132
                                    ipversion=ipversion, gateway=gateway,
133
                                    dhcp=dhcp)
126 134
    except:
127
        print "Error"
135
        return "Error"
128 136

  
129
    return HttpResponse("test")
137
    return HttpResponse(sub, status=200)
130 138

  
131 139

  
132 140
@api.api_method(http_method='GET', user_required=True, logger=log)
133
def get_subnet(request, offset):
141
def get_subnet(request, sub_id):
134 142
    '''Show info of a specific subnet'''
143
    log.debug('get_subnet %s', sub_id)
144
    user_id = request.user_uniq
145

  
135 146
    try:
136
        subnet = Subnet.objects.get(subnet_id=offset)
147
        subnet = Subnet.objects.get(id=sub_id)
137 148
    except Subnet.DoesNotExist:
138 149
        raise api.faults.ItemNotFound("Subnet not found")
139 150

  
140
    subnet_dic = subnet_to_dict(subnet)
141
    data = json.dumps({'subnet': subnet_dic})
151
    if subnet.network.userid != user_id:
152
        raise api.failts.Unauthorized("You're not allowed to view this subnet")
153

  
154
    subnet_dict = subnet_to_dict(subnet)
155
    data = json.dumps({'subnet': subnet_dict})
142 156
    return HttpResponse(data, status=200)
143 157

  
144 158

  
145 159
@api.api_method(http_method='DELETE', user_required=True, logger=log)
146
def delete_subnet(request, offset):
147
    '''Delete a subnet'''
148
    # Commented until we have a design document
149
    #log.info('delete_subnet %s', offset)
150
    #try:
151
    #    subnet = Subnet.objects.get(subnet_id=offset)
152
    #except Subnet.DoesNotExist:
153
    #    raise api.faults.ItemNotFound("Subnet not found")
154
        # Add support for 409 error, subnet in use
155

  
156
    #subnets.delete()
157
    #return HttpResponse(status=204)
160
def delete_subnet(request, sub_id):
161
    '''Delete a subnet -- Operation not allowed'''
158 162
    raise api.faults.BadRequest("Deletion of a subnet is not supported")
159 163

  
160 164

  
165
@api.api_method(http_method='PUT', user_required=True, logger=log)
166
def update_subnet(request, sub_id):
167
    '''Update info of a subnet'''
168

  
169

  
161 170
def subnet_to_dict(subnet):
162 171
    '''Returns a dictionary containing the info of a subnet'''
163 172
    # FIX ME, allocation pools
164
    dic = dict({'id': subnet.sunbet_id, 'network_id': subnet.network.id,
165
                'name': subnet.name, 'tenant_id': subnet.network.userid,
166
                'gateway_ip': subnet.gateway, 'ip_version': subnet.ipversion,
167
                'cidr': subnet.cidr, 'enable_dhcp': subnet.dhcp,
168
                'dns_nameservers': [], 'host_routes': [],
169
                'allocation_pools': [], 'SNF:gateway6': subnet.gateway6,
170
                'SNF:subnet6': subnet.subnet6})
171

  
172
#    dic = dict(id=subnet.subnet_id, network_id=subnet.network.id,
173
#               name=subnet.name, tenant_id=subnet.network.userid,
174
#               gateway_ip=subnet.gateway, ip_version=subnet.ipversion,
175
#               cidr=subnet.cidr, enable_dhcp=subnet.dhcp,
176
#               dns_nameservers=[], allocation_pools=[], host_routes=[],
177
#               snf-gateway6=subnet.gateway6, snf-subnet6=subnet.subnet6)
178

  
179
    return dic
173
    dictionary = dict({'id': subnet.id, 'network_id': subnet.network.id,
174
                       'name': subnet.name, 'tenant_id': subnet.network.userid,
175
                       'gateway_ip': subnet.gateway,
176
                       'ip_version': subnet.ipversion, 'cidr': subnet.cidr,
177
                       'enable_dhcp': subnet.dhcp, 'dns_nameservers': [],
178
                       'host_routes': [], 'allocation_pools': []})
179
    return dictionary
180

  
181

  
182
def check_number_of_subnets(network, version):
183
    '''Checks if a user can add a subnet in a network'''
184
    if network.subnet_set.filter(ipversion=version):
185
        raise api.faults.BadRequest("Only one subnet of IPv4/IPv6 per "
186
                                    "network is allowed")
b/snf-cyclades-app/synnefo/neutron/tests/api.py
116 116

  
117 117
class SubnetTest(BaseAPITest):
118 118
    def test_list_subnets(self):
119
        '''Test Subnet list'''
119
        '''Test list subnets without data'''
120 120
        response = self.get(SUBNETS_URL)
121 121
        self.assertSuccess(response)
122 122
        subnets = json.loads(response.content)
123 123
        self.assertEqual(subnets, {'subnets': []})
124 124

  
125
    def test_list_subnets_data(self):
126
        '''Test list subnets with data'''
127
        test_net = mf.NetworkFactory()
128
        test_subnet_ipv4 = mf.SubnetFactory(network=test_net)
129
        test_subnet_ipv6 = mf.SubnetFactory(network=test_net, ipversion=6,
130
                                            cidr='2620:0:2d0:200::7/32')
131
        response = self.get(SUBNETS_URL, user=test_net.userid)
132
        self.assertSuccess(response)
133

  
125 134
    def test_get_subnet(self):
126
        '''Test get info of a subnet'''
127
        url = join_urls(SUBNETS_URL, '42')
128
        response = self.get(url)
135
        '''Test get info of a single subnet'''
136
        test_net = mf.NetworkFactory()
137
        test_subnet = mf.SubnetFactory(network=test_net)
138
        url = join_urls(SUBNETS_URL, str(test_subnet.id))
139
        response = self.get(url, user=test_net.userid)
129 140
        self.assertSuccess(response)
130
        subnet = json.loads(response.content)
131
        self.assertEqual(subnet, {'subnet': []})
132 141

  
133 142
    def test_get_subnet_404(self):
134 143
        '''Test get info of a subnet that doesn't exist'''
135
        url = join_urls(SUBNETS_URL, '52')
144
        url = join_urls(SUBNETS_URL, '42')
136 145
        response = self.get(url)
137 146
        self.assertItemNotFound(response)
138 147

  
139
    #def test_subnet_delete_not_found(self):
140
     #   '''Test delete a subnet that doesn't exist'''
141
     #   # FIX ME
142
     #   url = join_urls(SUBNETS_URL, '52')
143
     #   response = self.get(url)
144
     #   self.assertItemNotFound(response)
145

  
146 148
    def test_subnet_delete(self):
147 149
        '''Test delete a subnet -- not supported'''
148 150
        url = join_urls(SUBNETS_URL, '42')
149
        response = self.get(url)
151
        response = self.delete(url)
152
        self.assertBadRequest(response)
153

  
154
   # def test_create_subnet_success(self):
155
   #     '''Test create a subnet successfully'''
156
   #     test_net = mf.NetworkFactory()
157
   #     request = {
158
   #         'subnet': {
159
   #             'network_id': test_net.id,
160
   #             'cidr': '10.0.3.0/24',
161
   #             'ip_version': 4}
162
   #     }
163
   #     response = self.post(SUBNETS_URL, test_net.userid,
164
   #                          json.dumps(request), "json")
165
   #     self.assertSuccess(response)
166

  
167
    def test_create_subnet_with_invalid_network_id(self):
168
        '''Test create a subnet with a network id that doesn't exist'''
169
        test_net = mf.NetworkFactory()
170
        request = {
171
            'subnet': {
172
                'network_id': '42',
173
                'cidr': '10.0.3.0/24',
174
                'ip_version': 4}
175
        }
176
        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
177
                             "json")
150 178
        self.assertItemNotFound(response)
151 179

  
152 180
    def test_create_subnet_with_malformed_ipversion(self):
153 181
        '''Create a subnet with a malformed ip_version type'''
182
        test_net = mf.NetworkFactory()
154 183
        request = {
155 184
            'subnet': {
156
                'network_id': 'ed2e3c10-2e43-4297-9006-2863a2d1abbc',
185
                'network_id': test_net.id,
157 186
                'cidr': '10.0.3.0/24',
158 187
                'ip_version': 8}
159 188
        }
160
        response = self.post(SUBNETS_URL, "user9", json.dumps(request), "json")
189
        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
190
                             "json")
161 191
        self.assertBadRequest(response)
162 192

  
163 193
    def test_create_subnet_with_invalid_cidr(self):
164 194
        '''Create a subnet with an invalid cidr'''
195
        test_net = mf.NetworkFactory()
165 196
        request = {
166 197
            'subnet': {
167
                'network_id': 'ed2e3c10-2e43-4297-9006-2863a2d1abbc',
198
                'network_id': test_net.id,
168 199
                'cidr': '192.168.3.0/8'}
169 200
        }
170
        response = self.post(SUBNETS_URL, "user9", json.dumps(request), "json")
201
        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
202
                             "json")
171 203
        self.assertBadRequest(response)
172 204

  
173 205
    def test_create_subnet_with_invalid_gateway(self):
174 206
        '''Create a subnet with a gateway outside of the subnet range'''
207
        test_net = mf.NetworkFactory()
175 208
        request = {
176 209
            'subnet': {
177
                'network_id': 'ed2e3c10-2e43-4297-9006-2863a2d1abbc',
210
                'network_id': test_net.id,
178 211
                'cidr': '192.168.3.0/24',
179 212
                'gateway_ip': '192.168.0.1'}
180 213
        }
181
        response = self.post(SUBNETS_URL, "user9", json.dumps(request), "json")
214
        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
215
                             "json")
182 216
        self.assertBadRequest(response)
183 217

  
184
    def test_create_subnet_with_long_name(self):
218
    def test_create_subnet_with_invalid_name(self):
185 219
        '''Create a subnet with an invalid subnet name'''
220
        test_net = mf.NetworkFactory()
186 221
        request = {
187 222
            'subnet': {
188
                'network_id': 'ed2e3c10-2e43-4297-9006-2863a2d1abbc',
223
                'network_id': test_net.id,
189 224
                'cidr': '192.168.3.0/24',
190
                'name': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
191
                        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
192
                        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'}
225
                'name': 'a' * 300}
193 226
        }
194
        response = self.post(SUBNETS_URL, "user9", json.dumps(request), "json")
227
        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
228
                             "json")
195 229
        self.assertBadRequest(response)
196 230

  
197 231
    def test_create_subnet_with_invalid_dhcp(self):
198 232
        '''Create a subnet with an invalid dhcp value'''
233
        test_net = mf.NetworkFactory()
199 234
        request = {
200 235
            'subnet': {
201
                'network_id': 'ed2e3c10-2e43-4297-9006-2863a2d1abbc',
236
                'network_id': test_net.id,
202 237
                'cidr': '192.168.3.0/24',
203 238
                'enable_dhcp': 'None'}
204 239
        }
205
        response = self.post(SUBNETS_URL, "user9", json.dumps(request), "json")
240
        response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
241
                             "json")
206 242
        self.assertBadRequest(response)

Also available in: Unified diff