mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
IPAddress.role to slug (#3569)
This commit is contained in:
@ -202,7 +202,7 @@ class IPAddressSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
status = ChoiceField(choices=IPAddressStatusChoices, required=False)
|
||||
role = ChoiceField(choices=IPADDRESS_ROLE_CHOICES, required=False, allow_null=True)
|
||||
role = ChoiceField(choices=IPAddressRoleChoices, required=False, allow_null=True)
|
||||
interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
|
||||
nat_inside = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||
nat_outside = NestedIPAddressSerializer(read_only=True)
|
||||
|
@ -51,3 +51,37 @@ class IPAddressStatusChoices(ChoiceSet):
|
||||
STATUS_DEPRECATED: 3,
|
||||
STATUS_DHCP: 5,
|
||||
}
|
||||
|
||||
|
||||
class IPAddressRoleChoices(ChoiceSet):
|
||||
|
||||
ROLE_LOOPBACK = 'loopback'
|
||||
ROLE_SECONDARY = 'secondary'
|
||||
ROLE_ANYCAST = 'anycast'
|
||||
ROLE_VIP = 'vip'
|
||||
ROLE_VRRP = 'vrrp'
|
||||
ROLE_HSRP = 'hsrp'
|
||||
ROLE_GLBP = 'glbp'
|
||||
ROLE_CARP = 'carp'
|
||||
|
||||
CHOICES = (
|
||||
(ROLE_LOOPBACK, 'Loopback'),
|
||||
(ROLE_SECONDARY, 'Secondary'),
|
||||
(ROLE_ANYCAST, 'Anycast'),
|
||||
(ROLE_VIP, 'VIP'),
|
||||
(ROLE_VRRP, 'VRRP'),
|
||||
(ROLE_HSRP, 'HSRP'),
|
||||
(ROLE_GLBP, 'GLBP'),
|
||||
(ROLE_CARP, 'CARP'),
|
||||
)
|
||||
|
||||
LEGACY_MAP = {
|
||||
ROLE_LOOPBACK: 10,
|
||||
ROLE_SECONDARY: 20,
|
||||
ROLE_ANYCAST: 30,
|
||||
ROLE_VIP: 40,
|
||||
ROLE_VRRP: 41,
|
||||
ROLE_HSRP: 42,
|
||||
ROLE_GLBP: 43,
|
||||
ROLE_CARP: 44,
|
||||
}
|
||||
|
@ -4,36 +4,6 @@ AF_CHOICES = (
|
||||
(6, 'IPv6'),
|
||||
)
|
||||
|
||||
# IP address roles
|
||||
IPADDRESS_ROLE_LOOPBACK = 10
|
||||
IPADDRESS_ROLE_SECONDARY = 20
|
||||
IPADDRESS_ROLE_ANYCAST = 30
|
||||
IPADDRESS_ROLE_VIP = 40
|
||||
IPADDRESS_ROLE_VRRP = 41
|
||||
IPADDRESS_ROLE_HSRP = 42
|
||||
IPADDRESS_ROLE_GLBP = 43
|
||||
IPADDRESS_ROLE_CARP = 44
|
||||
IPADDRESS_ROLE_CHOICES = (
|
||||
(IPADDRESS_ROLE_LOOPBACK, 'Loopback'),
|
||||
(IPADDRESS_ROLE_SECONDARY, 'Secondary'),
|
||||
(IPADDRESS_ROLE_ANYCAST, 'Anycast'),
|
||||
(IPADDRESS_ROLE_VIP, 'VIP'),
|
||||
(IPADDRESS_ROLE_VRRP, 'VRRP'),
|
||||
(IPADDRESS_ROLE_HSRP, 'HSRP'),
|
||||
(IPADDRESS_ROLE_GLBP, 'GLBP'),
|
||||
(IPADDRESS_ROLE_CARP, 'CARP'),
|
||||
)
|
||||
|
||||
IPADDRESS_ROLES_NONUNIQUE = (
|
||||
# IPAddress roles which are exempt from unique address enforcement
|
||||
IPADDRESS_ROLE_ANYCAST,
|
||||
IPADDRESS_ROLE_VIP,
|
||||
IPADDRESS_ROLE_VRRP,
|
||||
IPADDRESS_ROLE_HSRP,
|
||||
IPADDRESS_ROLE_GLBP,
|
||||
IPADDRESS_ROLE_CARP,
|
||||
)
|
||||
|
||||
# VLAN statuses
|
||||
VLAN_STATUS_ACTIVE = 1
|
||||
VLAN_STATUS_RESERVED = 2
|
||||
@ -53,16 +23,7 @@ STATUS_CHOICE_CLASSES = {
|
||||
4: 'warning',
|
||||
5: 'success',
|
||||
}
|
||||
ROLE_CHOICE_CLASSES = {
|
||||
10: 'default',
|
||||
20: 'primary',
|
||||
30: 'warning',
|
||||
40: 'success',
|
||||
41: 'success',
|
||||
42: 'success',
|
||||
43: 'success',
|
||||
44: 'success',
|
||||
}
|
||||
|
||||
|
||||
# IP protocols (for services)
|
||||
IP_PROTOCOL_TCP = 6
|
||||
|
@ -315,7 +315,7 @@ class IPAddressFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
null_value=None
|
||||
)
|
||||
role = django_filters.MultipleChoiceFilter(
|
||||
choices=IPADDRESS_ROLE_CHOICES
|
||||
choices=IPAddressRoleChoices
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
|
@ -769,7 +769,7 @@ class IPAddressCSVForm(forms.ModelForm):
|
||||
help_text='Operational status'
|
||||
)
|
||||
role = CSVChoiceField(
|
||||
choices=IPADDRESS_ROLE_CHOICES,
|
||||
choices=IPAddressRoleChoices,
|
||||
required=False,
|
||||
help_text='Functional role'
|
||||
)
|
||||
@ -899,7 +899,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
role = forms.ChoiceField(
|
||||
choices=add_blank_choice(IPADDRESS_ROLE_CHOICES),
|
||||
choices=add_blank_choice(IPAddressRoleChoices),
|
||||
required=False,
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
@ -978,7 +978,7 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
|
||||
widget=StaticSelect2Multiple()
|
||||
)
|
||||
role = forms.MultipleChoiceField(
|
||||
choices=IPADDRESS_ROLE_CHOICES,
|
||||
choices=IPAddressRoleChoices,
|
||||
required=False,
|
||||
widget=StaticSelect2Multiple()
|
||||
)
|
||||
|
@ -8,6 +8,17 @@ IPADDRESS_STATUS_CHOICES = (
|
||||
(3, 'deprecated'),
|
||||
)
|
||||
|
||||
IPADDRESS_ROLE_CHOICES = (
|
||||
(10, 'loopback'),
|
||||
(20, 'secondary'),
|
||||
(30, 'anycast'),
|
||||
(40, 'vip'),
|
||||
(41, 'vrrp'),
|
||||
(42, 'hsrp'),
|
||||
(43, 'glbp'),
|
||||
(44, 'carp'),
|
||||
)
|
||||
|
||||
|
||||
def ipaddress_status_to_slug(apps, schema_editor):
|
||||
IPAddress = apps.get_model('ipam', 'IPAddress')
|
||||
@ -15,6 +26,12 @@ def ipaddress_status_to_slug(apps, schema_editor):
|
||||
IPAddress.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
def ipaddress_role_to_slug(apps, schema_editor):
|
||||
IPAddress = apps.get_model('ipam', 'IPAddress')
|
||||
for id, slug in IPADDRESS_STATUS_CHOICES:
|
||||
IPAddress.objects.filter(role=str(id)).update(role=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
@ -34,4 +51,19 @@ class Migration(migrations.Migration):
|
||||
code=ipaddress_status_to_slug
|
||||
),
|
||||
|
||||
# IPAddress.role
|
||||
migrations.AlterField(
|
||||
model_name='ipaddress',
|
||||
name='role',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=ipaddress_role_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ipaddress',
|
||||
name='role',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
]
|
||||
|
@ -21,6 +21,17 @@ from .querysets import PrefixQuerySet
|
||||
from .validators import DNSValidator
|
||||
|
||||
|
||||
IPADDRESS_ROLES_NONUNIQUE = (
|
||||
# IPAddress roles which are exempt from unique address enforcement
|
||||
IPAddressRoleChoices.ROLE_ANYCAST,
|
||||
IPAddressRoleChoices.ROLE_VIP,
|
||||
IPAddressRoleChoices.ROLE_VRRP,
|
||||
IPAddressRoleChoices.ROLE_HSRP,
|
||||
IPAddressRoleChoices.ROLE_GLBP,
|
||||
IPAddressRoleChoices.ROLE_CARP,
|
||||
)
|
||||
|
||||
|
||||
class VRF(ChangeLoggedModel, CustomFieldModel):
|
||||
"""
|
||||
A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing
|
||||
@ -565,11 +576,10 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
||||
default=IPAddressStatusChoices.STATUS_ACTIVE,
|
||||
help_text='The operational status of this IP'
|
||||
)
|
||||
role = models.PositiveSmallIntegerField(
|
||||
verbose_name='Role',
|
||||
choices=IPADDRESS_ROLE_CHOICES,
|
||||
role = models.CharField(
|
||||
max_length=50,
|
||||
choices=IPAddressRoleChoices,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='The functional role of this IP'
|
||||
)
|
||||
interface = models.ForeignKey(
|
||||
@ -620,6 +630,17 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
||||
'dhcp': 'success',
|
||||
}
|
||||
|
||||
ROLE_CLASS_MAP = {
|
||||
'loopback': 'default',
|
||||
'secondary': 'primary',
|
||||
'anycast': 'warning',
|
||||
'vip': 'success',
|
||||
'vrrp': 'success',
|
||||
'hsrp': 'success',
|
||||
'glbp': 'success',
|
||||
'carp': 'success',
|
||||
}
|
||||
|
||||
class Meta:
|
||||
ordering = ['family', 'address']
|
||||
verbose_name = 'IP address'
|
||||
@ -756,7 +777,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
||||
return self.STATUS_CLASS_MAP.get(self.status)
|
||||
|
||||
def get_role_class(self):
|
||||
return ROLE_CHOICE_CLASSES[self.role]
|
||||
return self.ROLE_CLASS_MAP[self.role]
|
||||
|
||||
|
||||
class VLANGroup(ChangeLoggedModel):
|
||||
|
@ -2,7 +2,7 @@ import netaddr
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from ipam.constants import IPADDRESS_ROLE_VIP
|
||||
from ipam.choices import IPAddressRoleChoices
|
||||
from ipam.models import IPAddress, Prefix, VRF
|
||||
|
||||
|
||||
@ -61,5 +61,5 @@ class TestIPAddress(TestCase):
|
||||
|
||||
@override_settings(ENFORCE_GLOBAL_UNIQUE=True)
|
||||
def test_duplicate_nonunique_role(self):
|
||||
IPAddress.objects.create(address=netaddr.IPNetwork('192.0.2.1/24'), role=IPADDRESS_ROLE_VIP)
|
||||
IPAddress.objects.create(address=netaddr.IPNetwork('192.0.2.1/24'), role=IPADDRESS_ROLE_VIP)
|
||||
IPAddress.objects.create(address=netaddr.IPNetwork('192.0.2.1/24'), role=IPAddressRoleChoices.ROLE_VIP)
|
||||
IPAddress.objects.create(address=netaddr.IPNetwork('192.0.2.1/24'), role=IPAddressRoleChoices.ROLE_VIP)
|
||||
|
@ -14,7 +14,7 @@ from utilities.views import (
|
||||
)
|
||||
from virtualization.models import VirtualMachine
|
||||
from . import filters, forms, tables
|
||||
from .choices import PrefixStatusChoices
|
||||
from .choices import *
|
||||
from .constants import *
|
||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
||||
|
||||
@ -666,8 +666,8 @@ class IPAddressView(PermissionRequiredMixin, View):
|
||||
'nat_inside', 'interface__device'
|
||||
)
|
||||
# Exclude anycast IPs if this IP is anycast
|
||||
if ipaddress.role == IPADDRESS_ROLE_ANYCAST:
|
||||
duplicate_ips = duplicate_ips.exclude(role=IPADDRESS_ROLE_ANYCAST)
|
||||
if ipaddress.role == IPAddressRoleChoices.ROLE_ANYCAST:
|
||||
duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
|
||||
duplicate_ips_table = tables.IPAddressTable(list(duplicate_ips), orderable=False)
|
||||
|
||||
# Related IP table
|
||||
|
Reference in New Issue
Block a user