Coverage for ipam/models/fhrp.py: 84%

50 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation 

2from django.core.validators import MaxValueValidator, MinValueValidator 

3from django.db import models 

4from django.utils.translation import gettext_lazy as _ 

5 

6from ipam.choices import * 

7from ipam.constants import * 

8from netbox.models import ChangeLoggedModel, PrimaryModel 

9 

10__all__ = ( 

11 'FHRPGroup', 

12 'FHRPGroupAssignment', 

13) 

14 

15 

16class FHRPGroup(PrimaryModel): 

17 """ 

18 A grouping of next hope resolution protocol (FHRP) peers. (For instance, VRRP or HSRP.) 

19 """ 

20 group_id = models.PositiveSmallIntegerField( 

21 verbose_name=_('group ID') 

22 ) 

23 name = models.CharField( 

24 verbose_name=_('name'), 

25 max_length=100, 

26 blank=True 

27 ) 

28 protocol = models.CharField( 

29 verbose_name=_('protocol'), 

30 max_length=50, 

31 choices=FHRPGroupProtocolChoices 

32 ) 

33 auth_type = models.CharField( 

34 max_length=50, 

35 choices=FHRPGroupAuthTypeChoices, 

36 blank=True, 

37 null=True, 

38 verbose_name=_('authentication type') 

39 ) 

40 auth_key = models.CharField( 

41 max_length=255, 

42 blank=True, 

43 verbose_name=_('authentication key') 

44 ) 

45 ip_addresses = GenericRelation( 

46 to='ipam.IPAddress', 

47 content_type_field='assigned_object_type', 

48 object_id_field='assigned_object_id', 

49 related_query_name='fhrpgroup' 

50 ) 

51 services = GenericRelation( 

52 to='ipam.Service', 

53 content_type_field='parent_object_type', 

54 object_id_field='parent_object_id', 

55 related_query_name='fhrpgroup', 

56 ) 

57 

58 clone_fields = ('protocol', 'auth_type', 'auth_key', 'description') 

59 

60 class Meta: 

61 ordering = ['protocol', 'group_id', 'pk'] 

62 indexes = ( 

63 models.Index(fields=('protocol', 'group_id', 'id')), # Default ordering 

64 ) 

65 verbose_name = _('FHRP group') 

66 verbose_name_plural = _('FHRP groups') 

67 

68 def __str__(self): 

69 name = '' 

70 if self.name: 

71 name = f'{self.name} ' 

72 

73 name += f'{self.get_protocol_display()}: {self.group_id}' 

74 

75 # Append the first assigned IP addresses (if any) to serve as an additional identifier 

76 if self.pk: 76 ↛ 81line 76 didn't jump to line 81 because the condition on line 76 was always true

77 ip_address = self.ip_addresses.first() 

78 if ip_address: 78 ↛ 79line 78 didn't jump to line 79 because the condition on line 78 was never true

79 return f"{name} ({ip_address})" 

80 

81 return name 

82 

83 

84class FHRPGroupAssignment(ChangeLoggedModel): 

85 interface_type = models.ForeignKey( 

86 to='contenttypes.ContentType', 

87 on_delete=models.CASCADE 

88 ) 

89 interface_id = models.PositiveBigIntegerField() 

90 interface = GenericForeignKey( 

91 ct_field='interface_type', 

92 fk_field='interface_id' 

93 ) 

94 group = models.ForeignKey( 

95 to='ipam.FHRPGroup', 

96 on_delete=models.CASCADE 

97 ) 

98 priority = models.PositiveSmallIntegerField( 

99 verbose_name=_('priority'), 

100 validators=( 

101 MinValueValidator(FHRPGROUPASSIGNMENT_PRIORITY_MIN), 

102 MaxValueValidator(FHRPGROUPASSIGNMENT_PRIORITY_MAX) 

103 ) 

104 ) 

105 

106 class Meta: 

107 ordering = ('-priority', 'pk') 

108 indexes = ( 

109 models.Index(fields=('-priority', 'id')), # Default ordering 

110 models.Index(fields=('interface_type', 'interface_id')), 

111 ) 

112 constraints = ( 

113 models.UniqueConstraint( 

114 fields=('interface_type', 'interface_id', 'group'), 

115 name='%(app_label)s_%(class)s_unique_interface_group' 

116 ), 

117 ) 

118 verbose_name = _('FHRP group assignment') 

119 verbose_name_plural = _('FHRP group assignments') 

120 

121 def __str__(self): 

122 return f'{self.interface}: {self.group} ({self.priority})' 

123 

124 def get_absolute_url(self): 

125 # Used primarily for redirection after creating a new assignment 

126 if self.interface: 

127 return self.interface.get_absolute_url() 

128 return None