@@ -174,7 +174,11 @@ def test_field_validations(self):
174174 rule .full_clean ()
175175 expected_message_dict = {
176176 'number_of_subnets' : [
177- 'Master subnet cannot accommodate 99999999 subnets of size /28'
177+ 'The master subnet is too small to acommodate '
178+ 'the requested "number of subnets" plus the '
179+ 'reserved subnet, please increase the size of '
180+ 'the master subnet or decrease the '
181+ '"size of subnets" field.'
178182 ]
179183 }
180184 self .assertDictEqual (
@@ -246,6 +250,73 @@ def test_field_validations(self):
246250 {'organization' : ['Organization should be same as the subnet' ]},
247251 )
248252
253+ def test_slash_32_rule_ipv4 (self ):
254+ rule = self ._get_vpn_subdivision_rule (
255+ size = 32 , number_of_ips = 1 , number_of_subnets = 1
256+ )
257+ self .config .templates .add (self .template )
258+ rule .subnetdivisionindex_set .count ()
259+ index_queryset = rule .subnetdivisionindex_set .filter (
260+ config_id = self .config .id , subnet_id__isnull = False , ip_id__isnull = False
261+ )
262+ self .assertEqual (index_queryset .count (), 1 )
263+ index = index_queryset .first ()
264+ self .assertEqual (str (index .subnet .subnet ), '10.0.0.1/32' )
265+ self .assertEqual (index .ip .ip_address , '10.0.0.1' )
266+
267+ def test_slash_32_rule_ipv4_error (self ):
268+ master_ipv4 = self ._get_master_subnet (subnet = '192.168.1.1/32' )
269+ self .vpn_server .subnet = master_ipv4
270+ self .vpn_server .save ()
271+ try :
272+ self ._get_vpn_subdivision_rule (
273+ size = 32 , number_of_ips = 1 , number_of_subnets = 1 , master_subnet = master_ipv4
274+ )
275+ except ValidationError as e :
276+ self .assertIn ('number_of_subnets' , e .message_dict )
277+ self .assertIn (
278+ 'The master subnet is too small to acommodate' ,
279+ e .message_dict ['number_of_subnets' ][0 ],
280+ )
281+ else :
282+ self .fail ('Expected error not raised' )
283+
284+ def test_slash_128_rule_ipv6 (self ):
285+ master_ipv6 = self ._get_master_subnet (subnet = 'fd12:3456:7890::/48' )
286+ self .vpn_server .subnet = master_ipv6
287+ self .vpn_server .save ()
288+ rule = self ._get_vpn_subdivision_rule (
289+ size = 128 , number_of_ips = 1 , number_of_subnets = 1 , master_subnet = master_ipv6
290+ )
291+ self .config .templates .add (self .template )
292+ index_queryset = rule .subnetdivisionindex_set .filter (
293+ config_id = self .config .id , subnet_id__isnull = False , ip_id__isnull = False
294+ )
295+ self .assertEqual (index_queryset .count (), 1 )
296+ index = index_queryset .first ()
297+ self .assertEqual (str (index .subnet .subnet ), 'fd12:3456:7890::1/128' )
298+ self .assertEqual (index .ip .ip_address , 'fd12:3456:7890::1' )
299+
300+ def test_slash_128_rule_ipv6_error (self ):
301+ master_ipv6 = self ._get_master_subnet (subnet = 'fd12:3456:7890::/128' )
302+ self .vpn_server .subnet = master_ipv6
303+ self .vpn_server .save ()
304+ try :
305+ self ._get_vpn_subdivision_rule (
306+ size = 128 ,
307+ number_of_ips = 1 ,
308+ number_of_subnets = 1 ,
309+ master_subnet = master_ipv6 ,
310+ )
311+ except ValidationError as e :
312+ self .assertIn ('number_of_subnets' , e .message_dict )
313+ self .assertIn (
314+ 'The master subnet is too small to acommodate' ,
315+ e .message_dict ['number_of_subnets' ][0 ],
316+ )
317+ else :
318+ self .fail ('Expected error not raised' )
319+
249320 def test_rule_label_updated (self ):
250321 new_rule_label = 'TSDR'
251322 rule = self ._get_vpn_subdivision_rule (label = 'VPN_OW' )
@@ -418,11 +489,20 @@ def test_multiple_vpnclient_delete(self):
418489 @patch ('logging.Logger.error' )
419490 def test_subnets_exhausted (self , mocked_logger ):
420491 subnet = self ._get_master_subnet (
421- '10.0.0.0/28' , master_subnet = self .master_subnet
422- )
492+ '10.0.0.0/29' , master_subnet = self .master_subnet
493+ )
494+ # The master subnet can acommodate
495+ # this rule only once:
496+ # A /29 has 4 /31 slots available
497+ # Minus the reserved subnet = 3
498+ # Each run will eat 2 slots.
499+ # Hence we expect this to run fine the
500+ # first time but fail the second time.
423501 self ._get_vpn_subdivision_rule (
424502 master_subnet = subnet ,
425- size = 29 ,
503+ size = 31 ,
504+ number_of_ips = 2 ,
505+ number_of_subnets = 2 ,
426506 )
427507 self .vpn_server .subnet = subnet
428508 self .vpn_server .save ()
@@ -577,16 +657,94 @@ def test_device_subnet_division_rule(self):
577657 'backend' : 'netjsonconfig.OpenWrt' ,
578658 }
579659 response = self .client .post (reverse ('controller:device_register' ), options )
580- lines = response .content .decode ().split ('\n ' )
581- self .assertEqual (lines [0 ], 'registration-result: success' )
660+ self .assertEqual (response .status_code , 201 )
661+
662+ # Verify generated subnets and IP addresses match expectations
663+ self .assertEqual (
664+ subnet_query .count (),
665+ rule .number_of_subnets ,
666+ )
667+ self .assertEqual (
668+ self .ip_query .count (), (rule .number_of_subnets * rule .number_of_ips )
669+ )
670+ subnets = subnet_query .order_by ('created' )
671+ subnet1 = subnets [0 ]
672+ subnet2 = subnets [1 ]
673+ self .assertEqual (str (subnet1 .subnet ), '10.0.0.16/28' )
674+ self .assertEqual (str (subnet2 .subnet ), '10.0.0.32/28' )
675+ self .assertEqual (subnet1 .ipaddress_set .count (), 2 )
676+ self .assertEqual (subnet2 .ipaddress_set .count (), 2 )
677+ subnet1_ips = list (subnet1 .ipaddress_set .order_by ('created' ).all ())
678+ with self .subTest ('Check IP addresses of subnet1' ):
679+ self .assertEqual (str (subnet1_ips [0 ].ip_address ), '10.0.0.17' )
680+ self .assertEqual (str (subnet1_ips [1 ].ip_address ), '10.0.0.18' )
681+ subnet2_ips = list (subnet2 .ipaddress_set .order_by ('created' ).all ())
682+ with self .subTest ('Check IP addresses of subnet2' ):
683+ self .assertEqual (str (subnet2_ips [0 ].ip_address ), '10.0.0.33' )
684+ self .assertEqual (str (subnet2_ips [1 ].ip_address ), '10.0.0.34' )
685+
686+ # Verify context of config
687+ device = Device .objects .get (mac_address = 'FF:FF:FF:FF:FF:FF' )
688+ context = get_subnet_division_config_context (device .config )
689+ self .assertIn (f'{ rule .label } _prefixlen' , context )
690+ for subnet_id in range (1 , rule .number_of_subnets + 1 ):
691+ self .assertIn (f'{ rule .label } _subnet{ subnet_id } ' , context )
692+ for ip_id in range (1 , rule .number_of_ips + 1 ):
693+ self .assertIn (f'{ rule .label } _subnet{ subnet_id } _ip{ ip_id } ' , context )
694+
695+ # Verify working of delete handler
696+ device .delete ()
697+ self .assertEqual (
698+ subnet_query .count (),
699+ 0 ,
700+ )
701+ self .assertEqual (self .ip_query .count (), 0 )
702+
703+ def test_device_rule_use_entire_subnet (self ):
704+ self .config .delete ()
705+ rule = self ._get_device_subdivision_rule (size = 29 , number_of_ips = 8 )
706+ OrganizationConfigSettings .objects .create (
707+ organization = self .org , shared_secret = 'shared_secret'
708+ )
709+ subnet_query = self .subnet_query .filter (organization_id = self .org .id ).exclude (
710+ id = self .master_subnet .id
711+ )
712+ self .assertEqual (subnet_query .count (), 0 )
713+
714+ # Register device
715+ options = {
716+ 'hardware_id' : '1234' ,
717+ 'secret' : 'shared_secret' ,
718+ 'name' : 'FF:FF:FF:FF:FF:FF' ,
719+ 'mac_address' : 'FF:FF:FF:FF:FF:FF' ,
720+ 'backend' : 'netjsonconfig.OpenWrt' ,
721+ }
722+ response = self .client .post (reverse ('controller:device_register' ), options )
723+ self .assertEqual (response .status_code , 201 )
582724
725+ # Verify generated subnets and IP addresses match expectations
583726 self .assertEqual (
584727 subnet_query .count (),
585728 rule .number_of_subnets ,
586729 )
587730 self .assertEqual (
588731 self .ip_query .count (), (rule .number_of_subnets * rule .number_of_ips )
589732 )
733+ subnets = subnet_query .order_by ('created' )
734+ subnet1 = subnets [0 ]
735+ subnet2 = subnets [1 ]
736+ self .assertEqual (str (subnet1 .subnet ), '10.0.0.8/29' )
737+ self .assertEqual (str (subnet2 .subnet ), '10.0.0.16/29' )
738+ self .assertEqual (subnet1 .ipaddress_set .count (), 8 )
739+ self .assertEqual (subnet2 .ipaddress_set .count (), 8 )
740+
741+ number = 8
742+ for subnet in [subnet1 , subnet2 ]:
743+ for ip in subnet .ipaddress_set .order_by ('created' ).all ():
744+ expected_ip = f'10.0.0.{ number } '
745+ with self .subTest (f'Expect IP address: { expected_ip } ' ):
746+ self .assertEqual (str (ip .ip_address ), expected_ip )
747+ number += 1
590748
591749 # Verify context of config
592750 device = Device .objects .get (mac_address = 'FF:FF:FF:FF:FF:FF' )
0 commit comments