1
0
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:
Jeremy Stretch
2019-11-27 22:09:16 -05:00
parent ba8f324b12
commit 14a7a33cc2
9 changed files with 104 additions and 56 deletions

View File

@ -202,7 +202,7 @@ class IPAddressSerializer(TaggitSerializer, CustomFieldModelSerializer):
vrf = NestedVRFSerializer(required=False, allow_null=True) vrf = NestedVRFSerializer(required=False, allow_null=True)
tenant = NestedTenantSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True)
status = ChoiceField(choices=IPAddressStatusChoices, required=False) 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) interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
nat_inside = NestedIPAddressSerializer(required=False, allow_null=True) nat_inside = NestedIPAddressSerializer(required=False, allow_null=True)
nat_outside = NestedIPAddressSerializer(read_only=True) nat_outside = NestedIPAddressSerializer(read_only=True)

View File

@ -51,3 +51,37 @@ class IPAddressStatusChoices(ChoiceSet):
STATUS_DEPRECATED: 3, STATUS_DEPRECATED: 3,
STATUS_DHCP: 5, 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,
}

View File

@ -4,36 +4,6 @@ AF_CHOICES = (
(6, 'IPv6'), (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 statuses
VLAN_STATUS_ACTIVE = 1 VLAN_STATUS_ACTIVE = 1
VLAN_STATUS_RESERVED = 2 VLAN_STATUS_RESERVED = 2
@ -53,16 +23,7 @@ STATUS_CHOICE_CLASSES = {
4: 'warning', 4: 'warning',
5: 'success', 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 protocols (for services)
IP_PROTOCOL_TCP = 6 IP_PROTOCOL_TCP = 6

View File

@ -315,7 +315,7 @@ class IPAddressFilter(TenancyFilterSet, CustomFieldFilterSet):
null_value=None null_value=None
) )
role = django_filters.MultipleChoiceFilter( role = django_filters.MultipleChoiceFilter(
choices=IPADDRESS_ROLE_CHOICES choices=IPAddressRoleChoices
) )
tag = TagFilter() tag = TagFilter()

View File

@ -769,7 +769,7 @@ class IPAddressCSVForm(forms.ModelForm):
help_text='Operational status' help_text='Operational status'
) )
role = CSVChoiceField( role = CSVChoiceField(
choices=IPADDRESS_ROLE_CHOICES, choices=IPAddressRoleChoices,
required=False, required=False,
help_text='Functional role' help_text='Functional role'
) )
@ -899,7 +899,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
widget=StaticSelect2() widget=StaticSelect2()
) )
role = forms.ChoiceField( role = forms.ChoiceField(
choices=add_blank_choice(IPADDRESS_ROLE_CHOICES), choices=add_blank_choice(IPAddressRoleChoices),
required=False, required=False,
widget=StaticSelect2() widget=StaticSelect2()
) )
@ -978,7 +978,7 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
widget=StaticSelect2Multiple() widget=StaticSelect2Multiple()
) )
role = forms.MultipleChoiceField( role = forms.MultipleChoiceField(
choices=IPADDRESS_ROLE_CHOICES, choices=IPAddressRoleChoices,
required=False, required=False,
widget=StaticSelect2Multiple() widget=StaticSelect2Multiple()
) )

View File

@ -8,6 +8,17 @@ IPADDRESS_STATUS_CHOICES = (
(3, 'deprecated'), (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): def ipaddress_status_to_slug(apps, schema_editor):
IPAddress = apps.get_model('ipam', 'IPAddress') 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) 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): class Migration(migrations.Migration):
atomic = False atomic = False
@ -34,4 +51,19 @@ class Migration(migrations.Migration):
code=ipaddress_status_to_slug 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),
),
] ]

View File

@ -21,6 +21,17 @@ from .querysets import PrefixQuerySet
from .validators import DNSValidator 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): class VRF(ChangeLoggedModel, CustomFieldModel):
""" """
A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing 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, default=IPAddressStatusChoices.STATUS_ACTIVE,
help_text='The operational status of this IP' help_text='The operational status of this IP'
) )
role = models.PositiveSmallIntegerField( role = models.CharField(
verbose_name='Role', max_length=50,
choices=IPADDRESS_ROLE_CHOICES, choices=IPAddressRoleChoices,
blank=True, blank=True,
null=True,
help_text='The functional role of this IP' help_text='The functional role of this IP'
) )
interface = models.ForeignKey( interface = models.ForeignKey(
@ -620,6 +630,17 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
'dhcp': 'success', 'dhcp': 'success',
} }
ROLE_CLASS_MAP = {
'loopback': 'default',
'secondary': 'primary',
'anycast': 'warning',
'vip': 'success',
'vrrp': 'success',
'hsrp': 'success',
'glbp': 'success',
'carp': 'success',
}
class Meta: class Meta:
ordering = ['family', 'address'] ordering = ['family', 'address']
verbose_name = 'IP address' verbose_name = 'IP address'
@ -756,7 +777,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
return self.STATUS_CLASS_MAP.get(self.status) return self.STATUS_CLASS_MAP.get(self.status)
def get_role_class(self): def get_role_class(self):
return ROLE_CHOICE_CLASSES[self.role] return self.ROLE_CLASS_MAP[self.role]
class VLANGroup(ChangeLoggedModel): class VLANGroup(ChangeLoggedModel):

View File

@ -2,7 +2,7 @@ import netaddr
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.test import TestCase, override_settings 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 from ipam.models import IPAddress, Prefix, VRF
@ -61,5 +61,5 @@ class TestIPAddress(TestCase):
@override_settings(ENFORCE_GLOBAL_UNIQUE=True) @override_settings(ENFORCE_GLOBAL_UNIQUE=True)
def test_duplicate_nonunique_role(self): 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=IPAddressRoleChoices.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)

View File

@ -14,7 +14,7 @@ from utilities.views import (
) )
from virtualization.models import VirtualMachine from virtualization.models import VirtualMachine
from . import filters, forms, tables from . import filters, forms, tables
from .choices import PrefixStatusChoices from .choices import *
from .constants import * from .constants import *
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
@ -666,8 +666,8 @@ class IPAddressView(PermissionRequiredMixin, View):
'nat_inside', 'interface__device' 'nat_inside', 'interface__device'
) )
# Exclude anycast IPs if this IP is anycast # Exclude anycast IPs if this IP is anycast
if ipaddress.role == IPADDRESS_ROLE_ANYCAST: if ipaddress.role == IPAddressRoleChoices.ROLE_ANYCAST:
duplicate_ips = duplicate_ips.exclude(role=IPADDRESS_ROLE_ANYCAST) duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
duplicate_ips_table = tables.IPAddressTable(list(duplicate_ips), orderable=False) duplicate_ips_table = tables.IPAddressTable(list(duplicate_ips), orderable=False)
# Related IP table # Related IP table