~andreserl/maas/lp1592666

« back to all changes in this revision

Viewing changes to src/maasserver/models/tests/test_iprange.py

  • Committer: LaMont Jones
  • Date: 2016-04-11 16:23:26 UTC
  • mfrom: (4900 maas)
  • mto: This revision was merged to the branch mainline in revision 4924.
  • Revision ID: lamont@canonical.com-20160411162326-6ycj8l2j66v2o5es
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
__all__ = []
7
7
 
8
8
from django.core.exceptions import ValidationError
9
 
from maasserver.enum import IPRANGE_TYPE
 
9
from maasserver.enum import (
 
10
    IPADDRESS_TYPE,
 
11
    IPRANGE_TYPE,
 
12
)
10
13
from maasserver.models import IPRange
11
14
from maasserver.testing.factory import factory
12
15
from maasserver.testing.testcase import MAASServerTestCase
 
16
from maasserver.utils.orm import reload_object
13
17
from testtools import ExpectedException
14
18
 
15
19
 
 
20
def make_plain_subnet():
 
21
    return factory.make_Subnet(
 
22
        cidr='192.168.0.0/24',
 
23
        gateway_ip='192.168.0.1',
 
24
        dns_servers=[])
 
25
 
 
26
 
16
27
class IPRangeTest(MAASServerTestCase):
17
28
 
18
29
    def test__create(self):
19
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
30
        subnet = make_plain_subnet()
20
31
        iprange = IPRange(
21
 
            start_ip='192.168.0.1', end_ip='192.168.0.254',
 
32
            start_ip='192.168.0.2', end_ip='192.168.0.254',
22
33
            type=IPRANGE_TYPE.RESERVED, user=factory.make_User(),
23
34
            comment="The quick brown fox jumps over the lazy dog.",
24
35
            subnet=subnet)
25
36
        iprange.save()
26
37
 
27
38
    def test__requires_valid_ip_addresses(self):
28
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
39
        subnet = make_plain_subnet()
29
40
        iprange = IPRange(
30
41
            start_ip='x192.x168.x0.x1', end_ip='y192.y168.y0.y254',
31
42
            type=IPRANGE_TYPE.RESERVED, user=factory.make_User(),
35
46
            iprange.save()
36
47
 
37
48
    def test__requires_start_ip_address(self):
38
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
49
        subnet = make_plain_subnet()
39
50
        iprange = IPRange(
40
51
            start_ip='192.168.0.1', type=IPRANGE_TYPE.RESERVED,
41
52
            user=factory.make_User(), subnet=subnet,
44
55
            iprange.save()
45
56
 
46
57
    def test__requires_end_ip_address(self):
47
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
58
        subnet = make_plain_subnet()
48
59
        iprange = IPRange(
49
60
            end_ip='192.168.0.1', type=IPRANGE_TYPE.RESERVED,
50
61
            user=factory.make_User(), subnet=subnet,
53
64
            iprange.save()
54
65
 
55
66
    def test__requires_matching_address_family(self):
56
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
67
        subnet = make_plain_subnet()
57
68
        iprange = IPRange(
58
69
            start_ip='192.168.0.1', end_ip='2001:db8::1',
59
70
            type=IPRANGE_TYPE.RESERVED,
71
82
            iprange.save()
72
83
 
73
84
    def test__requires_start_ip_and_end_ip(self):
74
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
85
        subnet = make_plain_subnet()
75
86
        iprange = IPRange(
76
87
            subnet=subnet, type=IPRANGE_TYPE.RESERVED,
77
88
            user=factory.make_User(),
80
91
            iprange.save()
81
92
 
82
93
    def test__requires_start_ip_and_end_ip_to_be_within_subnet(self):
83
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
94
        subnet = make_plain_subnet()
84
95
        iprange = IPRange(
85
96
            start_ip='192.168.1.1', end_ip='192.168.1.254', subnet=subnet,
86
97
            type=IPRANGE_TYPE.RESERVED, user=factory.make_User(),
90
101
            iprange.save()
91
102
 
92
103
    def test__requires_start_ip_to_be_within_subnet(self):
93
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
104
        subnet = make_plain_subnet()
94
105
        iprange = IPRange(
95
106
            start_ip='19.168.0.1', end_ip='192.168.0.254', subnet=subnet,
96
107
            type=IPRANGE_TYPE.DYNAMIC, user=factory.make_User(),
100
111
            iprange.save()
101
112
 
102
113
    def test__requires_end_ip_to_be_within_subnet(self):
103
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
114
        subnet = make_plain_subnet()
104
115
        iprange = IPRange(
105
116
            start_ip='192.168.0.1', end_ip='193.168.0.254',
106
117
            subnet=subnet, type=IPRANGE_TYPE.DYNAMIC,
111
122
            iprange.save()
112
123
 
113
124
    def test__requires_end_ip_to_be_greater_or_equal_to_start_ip(self):
114
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
125
        subnet = make_plain_subnet()
115
126
        iprange = IPRange(
116
127
            start_ip='192.168.0.2', end_ip='192.168.0.1',
117
128
            user=factory.make_User(), subnet=subnet,
122
133
            iprange.save()
123
134
 
124
135
    def test__requires_type(self):
125
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
136
        subnet = make_plain_subnet()
126
137
        iprange = IPRange(
127
138
            start_ip='192.168.0.1', end_ip='192.168.0.254',
128
139
            user=factory.make_User(), subnet=subnet,
131
142
            iprange.save()
132
143
 
133
144
    def test__user_optional(self):
134
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
145
        subnet = make_plain_subnet()
135
146
        iprange = IPRange(
136
 
            start_ip='192.168.0.1', end_ip='192.168.0.254',
 
147
            start_ip='192.168.0.2', end_ip='192.168.0.254',
137
148
            type=IPRANGE_TYPE.DYNAMIC, subnet=subnet,
138
149
            comment="The quick brown owl jumps over the lazy alligator.")
139
150
        iprange.save()
140
151
 
141
152
    def test__comment_optional(self):
142
 
        subnet = factory.make_Subnet(cidr='192.168.0.0/24')
 
153
        subnet = make_plain_subnet()
143
154
        iprange = IPRange(
144
 
            start_ip='192.168.0.1', end_ip='192.168.0.254', subnet=subnet,
 
155
            start_ip='192.168.0.2', end_ip='192.168.0.254', subnet=subnet,
145
156
            type=IPRANGE_TYPE.RESERVED, user=factory.make_User())
146
157
        iprange.save()
 
158
 
 
159
 
 
160
class TestIPRangeSavePreventsOverlapping(MAASServerTestCase):
 
161
 
 
162
    no_fit = ".*No %s range can be created at requested start IP."
 
163
    dynamic_no_fit = no_fit % IPRANGE_TYPE.DYNAMIC
 
164
    reserved_no_fit = no_fit % IPRANGE_TYPE.RESERVED
 
165
 
 
166
    overlaps = ".*Requested %s range conflicts with an existing %srange.*"
 
167
    dynamic_overlaps = overlaps % (IPRANGE_TYPE.DYNAMIC, "IP address or ")
 
168
    reserved_overlaps = overlaps % (IPRANGE_TYPE.RESERVED, "")
 
169
 
 
170
    no_room = ".*There is no room for any %s ranges on this subnet.*"
 
171
    dynamic_no_room = no_room % IPRANGE_TYPE.DYNAMIC
 
172
    reserved_no_room = no_room % IPRANGE_TYPE.RESERVED
 
173
 
 
174
    def test__no_save_duplicate_ipranges(self):
 
175
        subnet = make_plain_subnet()
 
176
        IPRange(
 
177
            subnet=subnet,
 
178
            type=IPRANGE_TYPE.DYNAMIC,
 
179
            start_ip="192.168.0.100",
 
180
            end_ip="192.168.0.150",
 
181
        ).save()
 
182
        # Make the same range again, should fail to save.
 
183
        iprange = IPRange(
 
184
            subnet=subnet,
 
185
            type=IPRANGE_TYPE.DYNAMIC,
 
186
            start_ip="192.168.0.100",
 
187
            end_ip="192.168.0.150",
 
188
        )
 
189
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
190
            iprange.save()
 
191
 
 
192
    def test__no_save_range_overlap_begin(self):
 
193
        subnet = make_plain_subnet()
 
194
        IPRange(
 
195
            subnet=subnet,
 
196
            type=IPRANGE_TYPE.DYNAMIC,
 
197
            start_ip="192.168.0.100",
 
198
            end_ip="192.168.0.150",
 
199
        ).save()
 
200
        # Make an overlapping range across start_ip, should fail to save.
 
201
        iprange = IPRange(
 
202
            subnet=subnet,
 
203
            type=IPRANGE_TYPE.DYNAMIC,
 
204
            start_ip="192.168.0.90",
 
205
            end_ip="192.168.0.100",
 
206
        )
 
207
        with ExpectedException(ValidationError, self.dynamic_overlaps):
 
208
            iprange.save()
 
209
        # Try as reserved range.
 
210
        iprange.type = IPRANGE_TYPE.RESERVED
 
211
        with ExpectedException(ValidationError, self.reserved_overlaps):
 
212
            iprange.save()
 
213
 
 
214
    def test__no_save_range_overlap_end(self):
 
215
        subnet = make_plain_subnet()
 
216
        IPRange(
 
217
            subnet=subnet,
 
218
            type=IPRANGE_TYPE.DYNAMIC,
 
219
            start_ip="192.168.0.100",
 
220
            end_ip="192.168.0.150",
 
221
        ).save()
 
222
        # Make an overlapping range across end_ip, should fail to save.
 
223
        iprange = IPRange(
 
224
            subnet=subnet,
 
225
            type=IPRANGE_TYPE.DYNAMIC,
 
226
            start_ip="192.168.0.140",
 
227
            end_ip="192.168.0.160",
 
228
        )
 
229
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
230
            iprange.save()
 
231
 
 
232
    def test__no_save_range_within_ranges(self):
 
233
        subnet = make_plain_subnet()
 
234
        IPRange(
 
235
            subnet=subnet,
 
236
            type=IPRANGE_TYPE.DYNAMIC,
 
237
            start_ip="192.168.0.100",
 
238
            end_ip="192.168.0.150",
 
239
        ).save()
 
240
        # Make a contained range, should not save.
 
241
        iprange = IPRange(
 
242
            subnet=subnet,
 
243
            type=IPRANGE_TYPE.DYNAMIC,
 
244
            start_ip="192.168.0.110",
 
245
            end_ip="192.168.0.140",
 
246
        )
 
247
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
248
            iprange.save()
 
249
 
 
250
    def test__no_save_range_spanning_existing_range(self):
 
251
        subnet = make_plain_subnet()
 
252
        IPRange(
 
253
            subnet=subnet,
 
254
            type=IPRANGE_TYPE.DYNAMIC,
 
255
            start_ip="192.168.0.100",
 
256
            end_ip="192.168.0.150",
 
257
        ).save()
 
258
        # Make a contained range, should not save.
 
259
        iprange = IPRange(
 
260
            subnet=subnet,
 
261
            type=IPRANGE_TYPE.DYNAMIC,
 
262
            start_ip="192.168.0.10",
 
263
            end_ip="192.168.0.240",
 
264
        )
 
265
        with ExpectedException(ValidationError, self.dynamic_overlaps):
 
266
            iprange.save()
 
267
 
 
268
    def test__no_save_range_within_existing_range(self):
 
269
        subnet = make_plain_subnet()
 
270
        IPRange(
 
271
            subnet=subnet,
 
272
            type=IPRANGE_TYPE.DYNAMIC,
 
273
            start_ip="192.168.0.100",
 
274
            end_ip="192.168.0.150",
 
275
        ).save()
 
276
        # Make a contained range, should not save.
 
277
        iprange = IPRange(
 
278
            subnet=subnet,
 
279
            type=IPRANGE_TYPE.DYNAMIC,
 
280
            start_ip="192.168.0.110",
 
281
            end_ip="192.168.0.140",
 
282
        )
 
283
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
284
            iprange.save()
 
285
 
 
286
    def test__no_save_range_within_existing_reserved_range(self):
 
287
        subnet = make_plain_subnet()
 
288
        IPRange(
 
289
            subnet=subnet,
 
290
            type=IPRANGE_TYPE.RESERVED,
 
291
            start_ip="192.168.0.100",
 
292
            end_ip="192.168.0.150",
 
293
        ).save()
 
294
        # Make a contained range, should not save.
 
295
        iprange = IPRange(
 
296
            subnet=subnet,
 
297
            type=IPRANGE_TYPE.DYNAMIC,
 
298
            start_ip="192.168.0.110",
 
299
            end_ip="192.168.0.140",
 
300
        )
 
301
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
302
            iprange.save()
 
303
 
 
304
    def test__no_save_when_no_ranges_available(self):
 
305
        subnet = make_plain_subnet()
 
306
        # Reserve the whole subnet, except gateway.
 
307
        IPRange(
 
308
            subnet=subnet,
 
309
            type=IPRANGE_TYPE.RESERVED,
 
310
            start_ip="192.168.0.2",
 
311
            end_ip="192.168.0.254",
 
312
        ).save()
 
313
        # Try to make dynamic range at gateway (anywhere, actually) = no room!
 
314
        iprange = IPRange(
 
315
            subnet=subnet,
 
316
            type=IPRANGE_TYPE.DYNAMIC,
 
317
            start_ip="192.168.0.1",
 
318
            end_ip="192.168.0.1",
 
319
        )
 
320
        with ExpectedException(ValidationError, self.dynamic_no_room):
 
321
            iprange.save()
 
322
        # We CAN reserve the gateway addr.
 
323
        IPRange(
 
324
            subnet=subnet,
 
325
            type=IPRANGE_TYPE.RESERVED,
 
326
            start_ip="192.168.0.1",
 
327
            end_ip="192.168.0.1",
 
328
        ).save()
 
329
        # But now it's full - trying to save any reserved = no room!
 
330
        iprange = IPRange(
 
331
            subnet=subnet,
 
332
            type=IPRANGE_TYPE.RESERVED,
 
333
            start_ip="192.168.0.25",
 
334
            end_ip="192.168.0.35",
 
335
        )
 
336
        with ExpectedException(ValidationError, self.reserved_no_room):
 
337
            iprange.save()
 
338
 
 
339
    def test__modify_existing_performs_validation(self):
 
340
        subnet = make_plain_subnet()
 
341
        IPRange(
 
342
            subnet=subnet,
 
343
            type=IPRANGE_TYPE.DYNAMIC,
 
344
            start_ip="192.168.0.100",
 
345
            end_ip="192.168.0.150",
 
346
        ).save()
 
347
        iprange = IPRange(
 
348
            subnet=subnet,
 
349
            type=IPRANGE_TYPE.DYNAMIC,
 
350
            start_ip="192.168.0.151",
 
351
            end_ip="192.168.0.200",
 
352
        )
 
353
        iprange.save()
 
354
        # Make sure safe modification works.
 
355
        iprange.start_ip = "192.168.0.210"
 
356
        iprange.end_ip = "192.168.0.250"
 
357
        iprange.save()
 
358
        # Modify again, but conflict with first range this time.
 
359
        instance_id = iprange.id
 
360
        iprange.start_ip = "192.168.0.110"
 
361
        iprange.end_ip = "192.168.0.140"
 
362
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
363
            iprange.save()
 
364
        # Make sure original range isn't deleted after failure to modify.
 
365
        iprange = reload_object(iprange)
 
366
        self.assertEqual(iprange.id, instance_id)
 
367
 
 
368
    def test__dynamic_range_cant_overlap_gateway_ip(self):
 
369
        subnet = make_plain_subnet()
 
370
        iprange = IPRange(
 
371
            subnet=subnet,
 
372
            type=IPRANGE_TYPE.DYNAMIC,
 
373
            start_ip="192.168.0.2",
 
374
            end_ip="192.168.0.5",
 
375
        )
 
376
        iprange.save()
 
377
        # A DYNAMIC range cannot overlap the gateway IP.
 
378
        iprange.start_ip = "192.168.0.1"
 
379
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
380
            iprange.save()
 
381
 
 
382
    def test__reserved_range_can_overlap_gateway_ip(self):
 
383
        subnet = make_plain_subnet()
 
384
        iprange = IPRange(
 
385
            subnet=subnet,
 
386
            type=IPRANGE_TYPE.RESERVED,
 
387
            start_ip="192.168.0.2",
 
388
            end_ip="192.168.0.5",
 
389
        )
 
390
        iprange.save()
 
391
        # A RESERVED range can overlap the gateway IP.
 
392
        iprange.start_ip = "192.168.0.1"
 
393
        iprange.save()
 
394
 
 
395
    def test__reserved_range_cannot_overlap_dynamic_ranges(self):
 
396
        subnet = factory.make_Subnet(
 
397
            cidr='192.168.0.0/24',
 
398
            gateway_ip='192.168.0.1',
 
399
            dns_servers=['192.168.0.50', '192.168.0.200'])
 
400
        IPRange(
 
401
            subnet=subnet,
 
402
            type=IPRANGE_TYPE.DYNAMIC,
 
403
            start_ip="192.168.0.2",
 
404
            end_ip="192.168.0.49",
 
405
        ).save()
 
406
        iprange = IPRange(
 
407
            subnet=subnet,
 
408
            type=IPRANGE_TYPE.RESERVED,
 
409
            start_ip="192.168.0.25",
 
410
            end_ip="192.168.0.30",
 
411
        )
 
412
        with ExpectedException(ValidationError, self.reserved_no_fit):
 
413
            iprange.save()
 
414
 
 
415
    def test__reserved_range_cannot_overlap_reserved_ranges(self):
 
416
        subnet = factory.make_Subnet(
 
417
            cidr='192.168.0.0/24',
 
418
            gateway_ip='192.168.0.1',
 
419
            dns_servers=['192.168.0.50', '192.168.0.200'])
 
420
        IPRange(
 
421
            subnet=subnet,
 
422
            type=IPRANGE_TYPE.RESERVED,
 
423
            start_ip="192.168.0.1",
 
424
            end_ip="192.168.0.250",
 
425
        ).save()
 
426
        iprange = IPRange(
 
427
            subnet=subnet,
 
428
            type=IPRANGE_TYPE.RESERVED,
 
429
            start_ip="192.168.0.250",
 
430
            end_ip="192.168.0.254",
 
431
        )
 
432
        with ExpectedException(ValidationError, self.reserved_no_fit):
 
433
            iprange.save()
 
434
 
 
435
    def test__dynamic_range_cannot_overlap_static_ip(self):
 
436
        subnet = make_plain_subnet()
 
437
        factory.make_StaticIPAddress(
 
438
            alloc_type=IPADDRESS_TYPE.USER_RESERVED, subnet=subnet)
 
439
        iprange = IPRange(
 
440
            subnet=subnet,
 
441
            type=IPRANGE_TYPE.DYNAMIC,
 
442
            start_ip="192.168.0.2",
 
443
            end_ip="192.168.0.254",
 
444
        )
 
445
        with ExpectedException(ValidationError, self.dynamic_overlaps):
 
446
            iprange.save()
 
447
 
 
448
    def test__reserved_range_can_overlap_static_ip(self):
 
449
        subnet = make_plain_subnet()
 
450
        factory.make_StaticIPAddress(
 
451
            alloc_type=IPADDRESS_TYPE.USER_RESERVED, subnet=subnet)
 
452
        iprange = IPRange(
 
453
            subnet=subnet,
 
454
            type=IPRANGE_TYPE.RESERVED,
 
455
            start_ip="192.168.0.1",
 
456
            end_ip="192.168.0.254",
 
457
        )
 
458
        iprange.save()
 
459
 
 
460
    def test__dynamic_range_cannot_overlap_dns_servers(self):
 
461
        subnet = factory.make_Subnet(
 
462
            cidr='192.168.0.0/24',
 
463
            gateway_ip='192.168.0.1',
 
464
            dns_servers=['192.168.0.50', '192.168.0.200'])
 
465
        iprange = IPRange(
 
466
            subnet=subnet,
 
467
            type=IPRANGE_TYPE.DYNAMIC,
 
468
            start_ip="192.168.0.1",
 
469
            end_ip="192.168.0.254",
 
470
        )
 
471
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
472
            iprange.save()
 
473
 
 
474
    def test__reserved_range_can_overlap_dns_servers(self):
 
475
        subnet = factory.make_Subnet(
 
476
            cidr='192.168.0.0/24',
 
477
            gateway_ip='192.168.0.1',
 
478
            dns_servers=['192.168.0.50', '192.168.0.200'])
 
479
        iprange = IPRange(
 
480
            subnet=subnet,
 
481
            type=IPRANGE_TYPE.RESERVED,
 
482
            start_ip="192.168.0.1",
 
483
            end_ip="192.168.0.254",
 
484
        )
 
485
        iprange.save()
 
486
 
 
487
    def test__change_reserved_to_dynamic(self):
 
488
        subnet = make_plain_subnet()
 
489
        iprange = IPRange(
 
490
            subnet=subnet,
 
491
            type=IPRANGE_TYPE.RESERVED,
 
492
            start_ip="192.168.0.1",
 
493
            end_ip="192.168.0.5",
 
494
        )
 
495
        # Reserved should save OK overlapping gateway IP.
 
496
        iprange.save()
 
497
 
 
498
        # Dynamic should not save overlapping gateway IP.
 
499
        iprange.type = IPRANGE_TYPE.DYNAMIC
 
500
        with ExpectedException(ValidationError, self.dynamic_no_fit):
 
501
            iprange.save()
 
502
        # Fix start_ip and now it should save.
 
503
        iprange.start_ip = "192.168.0.2"
 
504
        iprange.save()
 
505
 
 
506
    def test__change_dynamic_to_reserved(self):
 
507
        subnet = make_plain_subnet()
 
508
        iprange = IPRange(
 
509
            subnet=subnet,
 
510
            type=IPRANGE_TYPE.DYNAMIC,
 
511
            start_ip="192.168.0.2",
 
512
            end_ip="192.168.0.5",
 
513
        )
 
514
        iprange.save()
 
515
        # Reserved should save OK overlapping gateway IP.
 
516
        iprange.type = IPRANGE_TYPE.RESERVED
 
517
        iprange.start_ip = "192.168.0.1"
 
518
        iprange.save()