diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 8792063aa..7aeb37a45 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -103,7 +103,7 @@ class VLANSerializer(TaggitSerializer, CustomFieldModelSerializer): site = NestedSiteSerializer(required=False, allow_null=True) group = NestedVLANGroupSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) - status = ChoiceField(choices=VLAN_STATUS_CHOICES, required=False) + status = ChoiceField(choices=VLANStatusChoices, required=False) role = NestedRoleSerializer(required=False, allow_null=True) tags = TagListSerializerField(required=False) prefix_count = serializers.IntegerField(read_only=True) diff --git a/netbox/ipam/choices.py b/netbox/ipam/choices.py index daf8aebec..840a9d8df 100644 --- a/netbox/ipam/choices.py +++ b/netbox/ipam/choices.py @@ -85,3 +85,26 @@ class IPAddressRoleChoices(ChoiceSet): ROLE_GLBP: 43, ROLE_CARP: 44, } + + +# +# VLANs +# + +class VLANStatusChoices(ChoiceSet): + + STATUS_ACTIVE = 'active' + STATUS_RESERVED = 'reserved' + STATUS_DEPRECATED = 'deprecated' + + CHOICES = ( + (STATUS_ACTIVE, 'Active'), + (STATUS_RESERVED, 'Reserved'), + (STATUS_DEPRECATED, 'Deprecated'), + ) + + LEGACY_MAP = { + STATUS_ACTIVE: 1, + STATUS_RESERVED: 2, + STATUS_DEPRECATED: 3, + } diff --git a/netbox/ipam/constants.py b/netbox/ipam/constants.py index 59e25489c..e68213550 100644 --- a/netbox/ipam/constants.py +++ b/netbox/ipam/constants.py @@ -4,26 +4,6 @@ AF_CHOICES = ( (6, 'IPv6'), ) -# VLAN statuses -VLAN_STATUS_ACTIVE = 1 -VLAN_STATUS_RESERVED = 2 -VLAN_STATUS_DEPRECATED = 3 -VLAN_STATUS_CHOICES = ( - (VLAN_STATUS_ACTIVE, 'Active'), - (VLAN_STATUS_RESERVED, 'Reserved'), - (VLAN_STATUS_DEPRECATED, 'Deprecated') -) - -# Bootstrap CSS classes -STATUS_CHOICE_CLASSES = { - 0: 'default', - 1: 'primary', - 2: 'info', - 3: 'danger', - 4: 'warning', - 5: 'success', -} - # IP protocols (for services) IP_PROTOCOL_TCP = 6 diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py index 26e5297bd..148b40269 100644 --- a/netbox/ipam/filters.py +++ b/netbox/ipam/filters.py @@ -425,7 +425,7 @@ class VLANFilter(TenancyFilterSet, CustomFieldFilterSet): label='Role (slug)', ) status = django_filters.MultipleChoiceFilter( - choices=VLAN_STATUS_CHOICES, + choices=VLANStatusChoices, null_value=None ) tag = TagFilter() diff --git a/netbox/ipam/fixtures/ipam.json b/netbox/ipam/fixtures/ipam.json index 9d8da496c..e722b3629 100644 --- a/netbox/ipam/fixtures/ipam.json +++ b/netbox/ipam/fixtures/ipam.json @@ -322,7 +322,7 @@ "site": 1, "vid": 999, "name": "TEST", - "status": 1, + "status": "active", "role": 1 } } diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 5bf620a5e..bacfbfcff 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -1112,7 +1112,7 @@ class VLANCSVForm(forms.ModelForm): } ) status = CSVChoiceField( - choices=VLAN_STATUS_CHOICES, + choices=VLANStatusChoices, help_text='Operational status' ) role = forms.ModelChoiceField( @@ -1181,7 +1181,7 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor ) ) status = forms.ChoiceField( - choices=add_blank_choice(VLAN_STATUS_CHOICES), + choices=add_blank_choice(VLANStatusChoices), required=False, widget=StaticSelect2() ) @@ -1230,7 +1230,7 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): ) ) status = forms.MultipleChoiceField( - choices=VLAN_STATUS_CHOICES, + choices=VLANStatusChoices, required=False, widget=StaticSelect2Multiple() ) diff --git a/netbox/ipam/migrations/0030_3569_vlan_fields.py b/netbox/ipam/migrations/0030_3569_vlan_fields.py new file mode 100644 index 000000000..ac3bc47ec --- /dev/null +++ b/netbox/ipam/migrations/0030_3569_vlan_fields.py @@ -0,0 +1,36 @@ +from django.db import migrations, models + + +VLAN_STATUS_CHOICES = ( + (1, 'active'), + (2, 'reserved'), + (3, 'deprecated'), +) + + +def vlan_status_to_slug(apps, schema_editor): + VLAN = apps.get_model('ipam', 'VLAN') + for id, slug in VLAN_STATUS_CHOICES: + VLAN.objects.filter(status=str(id)).update(status=slug) + + +class Migration(migrations.Migration): + atomic = False + + dependencies = [ + ('ipam', '0029_3569_ipaddress_fields'), + ] + + operations = [ + + # VLAN.status + migrations.AlterField( + model_name='vlan', + name='status', + field=models.CharField(default='active', max_length=50), + ), + migrations.RunPython( + code=vlan_status_to_slug + ), + + ] diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index 51431b483..00d9f5387 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -868,10 +868,10 @@ class VLAN(ChangeLoggedModel, CustomFieldModel): blank=True, null=True ) - status = models.PositiveSmallIntegerField( - choices=VLAN_STATUS_CHOICES, - default=1, - verbose_name='Status' + status = models.CharField( + max_length=50, + choices=VLANStatusChoices, + default=VLANStatusChoices.STATUS_ACTIVE ) role = models.ForeignKey( to='ipam.Role', @@ -894,6 +894,12 @@ class VLAN(ChangeLoggedModel, CustomFieldModel): csv_headers = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description'] + STATUS_CLASS_MAP = { + 'active': 'primary', + 'reserved': 'info', + 'deprecated': 'danger', + } + class Meta: ordering = ['site', 'group', 'vid'] unique_together = [ @@ -936,7 +942,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel): return None def get_status_class(self): - return STATUS_CHOICE_CLASSES[self.status] + return self.STATUS_CLASS_MAP[self.status] def get_members(self): # Return all interfaces assigned to this VLAN