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
« 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 _
6from ipam.choices import *
7from ipam.constants import *
8from netbox.models import ChangeLoggedModel, PrimaryModel
10__all__ = (
11 'FHRPGroup',
12 'FHRPGroupAssignment',
13)
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 )
58 clone_fields = ('protocol', 'auth_type', 'auth_key', 'description')
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')
68 def __str__(self):
69 name = ''
70 if self.name:
71 name = f'{self.name} '
73 name += f'{self.get_protocol_display()}: {self.group_id}'
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})"
81 return name
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 )
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')
121 def __str__(self):
122 return f'{self.interface}: {self.group} ({self.priority})'
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