Explorar o código

Adds rf_role to interface template (#13199)

* adds rf_role to interface template #13170

* fixed migration file conflict

* Misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
Abhimanyu Saharan %!s(int64=2) %!d(string=hai) anos
pai
achega
0f9fe96192

+ 7 - 1
netbox/dcim/api/serializers.py

@@ -514,12 +514,18 @@ class InterfaceTemplateSerializer(ValidatedModelSerializer):
         allow_blank=True,
         allow_blank=True,
         allow_null=True
         allow_null=True
     )
     )
+    rf_role = ChoiceField(
+        choices=WirelessRoleChoices,
+        required=False,
+        allow_blank=True,
+        allow_null=True
+    )
 
 
     class Meta:
     class Meta:
         model = InterfaceTemplate
         model = InterfaceTemplate
         fields = [
         fields = [
             'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only',
             'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only',
-            'description', 'bridge', 'poe_mode', 'poe_type', 'created', 'last_updated',
+            'description', 'bridge', 'poe_mode', 'poe_type', 'rf_role', 'created', 'last_updated',
         ]
         ]
 
 
 
 

+ 3 - 0
netbox/dcim/filtersets.py

@@ -696,6 +696,9 @@ class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCo
     poe_type = django_filters.MultipleChoiceFilter(
     poe_type = django_filters.MultipleChoiceFilter(
         choices=InterfacePoETypeChoices
         choices=InterfacePoETypeChoices
     )
     )
+    rf_role = django_filters.MultipleChoiceFilter(
+        choices=WirelessRoleChoices
+    )
 
 
     class Meta:
     class Meta:
         model = InterfaceTemplate
         model = InterfaceTemplate

+ 2 - 2
netbox/dcim/forms/bulk_create.py

@@ -76,14 +76,14 @@ class PowerOutletBulkCreateForm(
 
 
 class InterfaceBulkCreateForm(
 class InterfaceBulkCreateForm(
     form_from_model(Interface, [
     form_from_model(Interface, [
-        'type', 'enabled', 'speed', 'duplex', 'mtu', 'mgmt_only', 'mark_connected', 'poe_mode', 'poe_type',
+        'type', 'enabled', 'speed', 'duplex', 'mtu', 'mgmt_only', 'mark_connected', 'poe_mode', 'poe_type', 'rf_role'
     ]),
     ]),
     DeviceBulkAddComponentForm
     DeviceBulkAddComponentForm
 ):
 ):
     model = Interface
     model = Interface
     field_order = (
     field_order = (
         'name', 'label', 'type', 'enabled', 'speed', 'duplex', 'mtu', 'mgmt_only', 'poe_mode',
         'name', 'label', 'type', 'enabled', 'speed', 'duplex', 'mtu', 'mgmt_only', 'poe_mode',
-        'poe_type', 'mark_connected', 'description', 'tags',
+        'poe_type', 'mark_connected', 'rf_role', 'description', 'tags',
     )
     )
 
 
 
 

+ 8 - 1
netbox/dcim/forms/bulk_edit.py

@@ -15,6 +15,7 @@ from utilities.forms import BulkEditForm, add_blank_choice, form_from_model
 from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
 from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
 from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions
 from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions
 from wireless.models import WirelessLAN, WirelessLANGroup
 from wireless.models import WirelessLAN, WirelessLANGroup
+from wireless.choices import WirelessRoleChoices
 
 
 __all__ = (
 __all__ = (
     'CableBulkEditForm',
     'CableBulkEditForm',
@@ -922,8 +923,14 @@ class InterfaceTemplateBulkEditForm(BulkEditForm):
         initial='',
         initial='',
         label=_('PoE type')
         label=_('PoE type')
     )
     )
+    rf_role = forms.ChoiceField(
+        choices=add_blank_choice(WirelessRoleChoices),
+        required=False,
+        initial='',
+        label=_('Wireless role')
+    )
 
 
-    nullable_fields = ('label', 'description', 'poe_mode', 'poe_type')
+    nullable_fields = ('label', 'description', 'poe_mode', 'poe_type', 'rf_role')
 
 
 
 
 class FrontPortTemplateBulkEditForm(BulkEditForm):
 class FrontPortTemplateBulkEditForm(BulkEditForm):

+ 3 - 2
netbox/dcim/forms/model_forms.py

@@ -826,13 +826,14 @@ class InterfaceTemplateForm(ModularComponentTemplateForm):
 
 
     fieldsets = (
     fieldsets = (
         (None, ('device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only', 'description', 'bridge')),
         (None, ('device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only', 'description', 'bridge')),
-        ('PoE', ('poe_mode', 'poe_type'))
+        ('PoE', ('poe_mode', 'poe_type')),
+        ('Wireless', ('rf_role',))
     )
     )
 
 
     class Meta:
     class Meta:
         model = InterfaceTemplate
         model = InterfaceTemplate
         fields = [
         fields = [
-            'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'enabled', 'description', 'poe_mode', 'poe_type', 'bridge',
+            'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'enabled', 'description', 'poe_mode', 'poe_type', 'bridge', 'rf_role',
         ]
         ]
 
 
 
 

+ 8 - 1
netbox/dcim/forms/object_import.py

@@ -4,6 +4,7 @@ from django.utils.translation import gettext as _
 from dcim.choices import InterfacePoEModeChoices, InterfacePoETypeChoices, InterfaceTypeChoices, PortTypeChoices
 from dcim.choices import InterfacePoEModeChoices, InterfacePoETypeChoices, InterfaceTypeChoices, PortTypeChoices
 from dcim.models import *
 from dcim.models import *
 from utilities.forms import BootstrapMixin
 from utilities.forms import BootstrapMixin
+from wireless.choices import WirelessRoleChoices
 
 
 __all__ = (
 __all__ = (
     'ConsolePortTemplateImportForm',
     'ConsolePortTemplateImportForm',
@@ -96,11 +97,17 @@ class InterfaceTemplateImportForm(ComponentTemplateImportForm):
         required=False,
         required=False,
         label=_('PoE type')
         label=_('PoE type')
     )
     )
+    rf_role = forms.ChoiceField(
+        choices=WirelessRoleChoices,
+        required=False,
+        label=_('Wireless role')
+    )
 
 
     class Meta:
     class Meta:
         model = InterfaceTemplate
         model = InterfaceTemplate
         fields = [
         fields = [
-            'device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only', 'description', 'poe_mode', 'poe_type',
+            'device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only', 'description', 'poe_mode',
+            'poe_type', 'rf_role'
         ]
         ]
 
 
 
 

+ 3 - 0
netbox/dcim/graphql/types.py

@@ -277,6 +277,9 @@ class InterfaceTemplateType(ComponentTemplateObjectType):
     def resolve_poe_type(self, info):
     def resolve_poe_type(self, info):
         return self.poe_type or None
         return self.poe_type or None
 
 
+    def resolve_rf_role(self, info):
+        return self.rf_role or None
+
 
 
 class InventoryItemType(ComponentObjectType):
 class InventoryItemType(ComponentObjectType):
     component = graphene.Field('dcim.graphql.gfk_mixins.InventoryItemComponentType')
     component = graphene.Field('dcim.graphql.gfk_mixins.InventoryItemComponentType')

+ 18 - 0
netbox/dcim/migrations/0179_interfacetemplate_rf_role.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.2.2 on 2023-07-18 07:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0178_virtual_chassis_member_counter'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interfacetemplate',
+            name='rf_role',
+            field=models.CharField(blank=True, max_length=30),
+        ),
+    ]

+ 14 - 0
netbox/dcim/models/device_component_templates.py

@@ -13,6 +13,7 @@ from utilities.fields import ColorField, NaturalOrderingField
 from utilities.mptt import TreeManager
 from utilities.mptt import TreeManager
 from utilities.ordering import naturalize_interface
 from utilities.ordering import naturalize_interface
 from utilities.tracking import TrackingModelMixin
 from utilities.tracking import TrackingModelMixin
+from wireless.choices import WirelessRoleChoices
 from .device_components import (
 from .device_components import (
     ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PowerOutlet, PowerPort,
     ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PowerOutlet, PowerPort,
     RearPort,
     RearPort,
@@ -388,6 +389,12 @@ class InterfaceTemplate(ModularComponentTemplateModel):
         blank=True,
         blank=True,
         verbose_name='PoE type'
         verbose_name='PoE type'
     )
     )
+    rf_role = models.CharField(
+        max_length=30,
+        choices=WirelessRoleChoices,
+        blank=True,
+        verbose_name='Wireless role'
+    )
 
 
     component_model = Interface
     component_model = Interface
 
 
@@ -406,6 +413,11 @@ class InterfaceTemplate(ModularComponentTemplateModel):
                     'bridge': f"Bridge interface ({self.bridge}) must belong to the same module type"
                     'bridge': f"Bridge interface ({self.bridge}) must belong to the same module type"
                 })
                 })
 
 
+        if self.rf_role and self.type not in WIRELESS_IFACE_TYPES:
+            raise ValidationError({
+                'rf_role': "Wireless role may be set only on wireless interfaces."
+            })
+
     def instantiate(self, **kwargs):
     def instantiate(self, **kwargs):
         return self.component_model(
         return self.component_model(
             name=self.resolve_name(kwargs.get('module')),
             name=self.resolve_name(kwargs.get('module')),
@@ -415,6 +427,7 @@ class InterfaceTemplate(ModularComponentTemplateModel):
             mgmt_only=self.mgmt_only,
             mgmt_only=self.mgmt_only,
             poe_mode=self.poe_mode,
             poe_mode=self.poe_mode,
             poe_type=self.poe_type,
             poe_type=self.poe_type,
+            rf_role=self.rf_role,
             **kwargs
             **kwargs
         )
         )
     instantiate.do_not_call_in_templates = True
     instantiate.do_not_call_in_templates = True
@@ -430,6 +443,7 @@ class InterfaceTemplate(ModularComponentTemplateModel):
             'bridge': self.bridge.name if self.bridge else None,
             'bridge': self.bridge.name if self.bridge else None,
             'poe_mode': self.poe_mode,
             'poe_mode': self.poe_mode,
             'poe_type': self.poe_type,
             'poe_type': self.poe_type,
+            'rf_role': self.rf_role,
         }
         }
 
 
 
 

+ 4 - 1
netbox/dcim/tables/devicetypes.py

@@ -219,7 +219,10 @@ class InterfaceTemplateTable(ComponentTemplateTable):
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
         model = models.InterfaceTemplate
         model = models.InterfaceTemplate
-        fields = ('pk', 'name', 'label', 'enabled', 'mgmt_only', 'type', 'description', 'bridge', 'poe_mode', 'poe_type', 'actions')
+        fields = (
+            'pk', 'name', 'label', 'enabled', 'mgmt_only', 'type', 'description', 'bridge', 'poe_mode', 'poe_type',
+            'rf_role', 'actions',
+        )
         empty_text = "None"
         empty_text = "None"