From e3820e93b7c3def8483a83649a0c2ab7c5f97e8f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 23 Jun 2020 16:39:43 -0400 Subject: [PATCH] Misc cleanup, renaming --- netbox/dcim/models/device_components.py | 6 +- netbox/ipam/constants.py | 2 +- netbox/ipam/filters.py | 1 + netbox/ipam/forms.py | 1 + .../migrations/0037_ipaddress_assignment.py | 2 +- netbox/ipam/models.py | 10 ++- netbox/ipam/tables.py | 14 +--- netbox/ipam/views.py | 76 +++++++++---------- netbox/utilities/filters.py | 4 - netbox/virtualization/api/views.py | 2 +- netbox/virtualization/filters.py | 4 +- netbox/virtualization/forms.py | 10 +-- netbox/virtualization/models.py | 4 + netbox/virtualization/tables.py | 2 +- netbox/virtualization/tests/test_api.py | 2 +- netbox/virtualization/tests/test_filters.py | 2 +- netbox/virtualization/tests/test_views.py | 10 +-- netbox/virtualization/views.py | 22 +++--- 18 files changed, 82 insertions(+), 92 deletions(-) diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index fdbeeade8..30a276c7d 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -622,8 +622,7 @@ class BaseInterface(models.Model): @extras_features('graphs', 'export_templates', 'webhooks') class Interface(CableTermination, ComponentModel, BaseInterface): """ - A network interface within a Device. A physical Interface can connect to exactly one other - Interface. + A network interface within a Device. A physical Interface can connect to exactly one other Interface. """ device = models.ForeignKey( to='Device', @@ -695,8 +694,7 @@ class Interface(CableTermination, ComponentModel, BaseInterface): tags = TaggableManager(through=TaggedItem) csv_headers = [ - 'device', 'name', 'lag', 'type', 'enabled', 'mac_address', 'mtu', 'mgmt_only', - 'description', 'mode', + 'device', 'name', 'lag', 'type', 'enabled', 'mac_address', 'mtu', 'mgmt_only', 'description', 'mode', ] class Meta: diff --git a/netbox/ipam/constants.py b/netbox/ipam/constants.py index 0a3c67f32..1ad355aec 100644 --- a/netbox/ipam/constants.py +++ b/netbox/ipam/constants.py @@ -33,7 +33,7 @@ PREFIX_LENGTH_MAX = 127 # IPv6 IPADDRESS_ASSIGNMENT_MODELS = Q( Q(app_label='dcim', model='interface') | - Q(app_label='virtualization', model='interface') + Q(app_label='virtualization', model='vminterface') ) IPADDRESS_MASK_LENGTH_MIN = 1 diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py index c9012cd3a..0876a0d1a 100644 --- a/netbox/ipam/filters.py +++ b/netbox/ipam/filters.py @@ -319,6 +319,7 @@ class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, field_name='pk', label='Virtual machine (ID)', ) + # TODO: Restore filtering by assigned interface # interface = django_filters.ModelMultipleChoiceFilter( # field_name='interface__name', # queryset=Interface.objects.unrestricted(), diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index db9cc512e..ecb2d7c1e 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -522,6 +522,7 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm) # class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModelForm): + # TODO: Restore ability to select assigned object when editing IPAddress # interface = forms.ModelChoiceField( # queryset=Interface.objects.all(), # required=False diff --git a/netbox/ipam/migrations/0037_ipaddress_assignment.py b/netbox/ipam/migrations/0037_ipaddress_assignment.py index 607f832a5..6139d41d6 100644 --- a/netbox/ipam/migrations/0037_ipaddress_assignment.py +++ b/netbox/ipam/migrations/0037_ipaddress_assignment.py @@ -31,7 +31,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='ipaddress', name='assigned_object_type', - field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')), models.Q(('app_label', 'virtualization'), ('model', 'interface')), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType', blank=True, null=True), + field=models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')), models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), _connector='OR')), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType'), preserve_default=False, ), migrations.RunPython( diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index c7baba435..d2df6261f 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -753,6 +753,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel): super().save(*args, **kwargs) def to_objectchange(self, action): + # Annotate the assigned object, if any return ObjectChange( changed_object=self, object_repr=str(self), @@ -764,12 +765,15 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel): def to_csv(self): # Determine if this IP is primary for a Device + is_primary = False if self.address.version == 4 and getattr(self, 'primary_ip4_for', False): is_primary = True elif self.address.version == 6 and getattr(self, 'primary_ip6_for', False): is_primary = True - else: - is_primary = False + + obj_type = None + if self.assigned_object_type: + obj_type = f'{self.assigned_object_type.app_label}.{self.assigned_object_type.model}' return ( self.address, @@ -777,7 +781,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel): self.tenant.name if self.tenant else None, self.get_status_display(), self.get_role_display(), - '{}.{}'.format(self.assigned_object_type.app_label, self.assigned_object_type.model) if self.assigned_object_type else None, + obj_type, self.assigned_object_id, is_primary, self.dns_name, diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index 064b8d7ce..f1855327b 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -92,14 +92,6 @@ IPADDRESS_ASSIGN_LINK = """ {% endif %} """ -IPADDRESS_PARENT = """ -{% if record.interface %} - {{ record.interface.parent }} -{% else %} - — -{% endif %} -""" - VRF_LINK = """ {% if record.vrf %} {{ record.vrf }} @@ -477,17 +469,13 @@ class IPAddressAssignTable(BaseTable): status = tables.TemplateColumn( template_code=STATUS_LABEL ) - parent = tables.TemplateColumn( - template_code=IPADDRESS_PARENT, - orderable=False - ) assigned_object = tables.Column( orderable=False ) class Meta(BaseTable.Meta): model = IPAddress - fields = ('address', 'dns_name', 'vrf', 'status', 'role', 'tenant', 'parent', 'assigned_object', 'description') + fields = ('address', 'dns_name', 'vrf', 'status', 'role', 'tenant', 'assigned_object', 'description') orderable = False diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index bb8de1f73..bcbe00923 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -607,47 +607,47 @@ class IPAddressView(ObjectView): ipaddress = get_object_or_404(self.queryset, pk=pk) - # # Parent prefixes table - # parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter( - # vrf=ipaddress.vrf, prefix__net_contains=str(ipaddress.address.ip) - # ).prefetch_related( - # 'site', 'role' - # ) - # parent_prefixes_table = tables.PrefixTable(list(parent_prefixes), orderable=False) - # parent_prefixes_table.exclude = ('vrf',) - # - # # Duplicate IPs table - # duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter( - # vrf=ipaddress.vrf, address=str(ipaddress.address) - # ).exclude( - # pk=ipaddress.pk - # ).prefetch_related( - # 'nat_inside' - # ) - # # Exclude anycast IPs if this IP is 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 - # related_ips = IPAddress.objects.restrict(request.user, 'view').exclude( - # address=str(ipaddress.address) - # ).filter( - # vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address) - # ) - # related_ips_table = tables.IPAddressTable(related_ips, orderable=False) - # - # paginate = { - # 'paginator_class': EnhancedPaginator, - # 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT) - # } - # RequestConfig(request, paginate).configure(related_ips_table) + # Parent prefixes table + parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter( + vrf=ipaddress.vrf, prefix__net_contains=str(ipaddress.address.ip) + ).prefetch_related( + 'site', 'role' + ) + parent_prefixes_table = tables.PrefixTable(list(parent_prefixes), orderable=False) + parent_prefixes_table.exclude = ('vrf',) + + # Duplicate IPs table + duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter( + vrf=ipaddress.vrf, address=str(ipaddress.address) + ).exclude( + pk=ipaddress.pk + ).prefetch_related( + 'nat_inside' + ) + # Exclude anycast IPs if this IP is 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 + related_ips = IPAddress.objects.restrict(request.user, 'view').exclude( + address=str(ipaddress.address) + ).filter( + vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address) + ) + related_ips_table = tables.IPAddressTable(related_ips, orderable=False) + + paginate = { + 'paginator_class': EnhancedPaginator, + 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT) + } + RequestConfig(request, paginate).configure(related_ips_table) return render(request, 'ipam/ipaddress.html', { 'ipaddress': ipaddress, - # 'parent_prefixes_table': parent_prefixes_table, - # 'duplicate_ips_table': duplicate_ips_table, - # 'related_ips_table': related_ips_table, + 'parent_prefixes_table': parent_prefixes_table, + 'duplicate_ips_table': duplicate_ips_table, + 'related_ips_table': related_ips_table, }) diff --git a/netbox/utilities/filters.py b/netbox/utilities/filters.py index 2b49dd99e..f628ca917 100644 --- a/netbox/utilities/filters.py +++ b/netbox/utilities/filters.py @@ -256,10 +256,6 @@ class BaseFilterSet(django_filters.FilterSet): except django_filters.exceptions.FieldLookupError: # The filter could not be created because the lookup expression is not supported on the field continue - except Exception as e: - print(existing_filter_name, existing_filter) - print(f'field: {field}, lookup_expr: {lookup_expr}') - raise e if lookup_name.startswith('n'): # This is a negation filter which requires a queryset.exclude() clause diff --git a/netbox/virtualization/api/views.py b/netbox/virtualization/api/views.py index 3a720e56f..d5fff805b 100644 --- a/netbox/virtualization/api/views.py +++ b/netbox/virtualization/api/views.py @@ -78,4 +78,4 @@ class InterfaceViewSet(ModelViewSet): 'virtual_machine', 'tags' ) serializer_class = serializers.VMInterfaceSerializer - filterset_class = filters.InterfaceFilterSet + filterset_class = filters.VMInterfaceFilterSet diff --git a/netbox/virtualization/filters.py b/netbox/virtualization/filters.py index 50bde1b3f..33ca44a22 100644 --- a/netbox/virtualization/filters.py +++ b/netbox/virtualization/filters.py @@ -15,8 +15,8 @@ __all__ = ( 'ClusterFilterSet', 'ClusterGroupFilterSet', 'ClusterTypeFilterSet', - 'InterfaceFilterSet', 'VirtualMachineFilterSet', + 'VMInterfaceFilterSet', ) @@ -201,7 +201,7 @@ class VirtualMachineFilterSet( ) -class InterfaceFilterSet(BaseFilterSet): +class VMInterfaceFilterSet(BaseFilterSet): q = django_filters.CharFilter( method='search', label='Search', diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index ec4b28f04..1a304931c 100644 --- a/netbox/virtualization/forms.py +++ b/netbox/virtualization/forms.py @@ -571,7 +571,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil # VM interfaces # -class InterfaceForm(BootstrapMixin, forms.ModelForm): +class VMInterfaceForm(BootstrapMixin, forms.ModelForm): untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, @@ -643,7 +643,7 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm): self.cleaned_data['tagged_vlans'] = [] -class InterfaceCreateForm(BootstrapMixin, forms.Form): +class VMInterfaceCreateForm(BootstrapMixin, forms.Form): virtual_machine = forms.ModelChoiceField( queryset=VirtualMachine.objects.all(), widget=forms.HiddenInput() @@ -715,7 +715,7 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form): self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', site.pk) -class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): +class VMInterfaceBulkEditForm(BootstrapMixin, BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=VMInterface.objects.all(), widget=forms.MultipleHiddenInput() @@ -785,7 +785,7 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', site.pk) -class InterfaceFilterForm(forms.Form): +class VMInterfaceFilterForm(forms.Form): model = VMInterface enabled = forms.NullBooleanField( required=False, @@ -815,7 +815,7 @@ class VirtualMachineBulkAddComponentForm(BootstrapMixin, forms.Form): return ','.join(self.cleaned_data.get('tags')) -class InterfaceBulkCreateForm( +class VMInterfaceBulkCreateForm( form_from_model(VMInterface, ['enabled', 'mtu', 'description', 'tags']), VirtualMachineBulkAddComponentForm ): diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index 31ffd1ceb..bed198326 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -475,6 +475,10 @@ class VMInterface(BaseInterface): object_data=serialize_object(self) ) + @property + def parent(self): + return self.virtual_machine + @property def count_ipaddresses(self): return self.ip_addresses.count() diff --git a/netbox/virtualization/tables.py b/netbox/virtualization/tables.py index e06714e85..fd6395220 100644 --- a/netbox/virtualization/tables.py +++ b/netbox/virtualization/tables.py @@ -172,7 +172,7 @@ class VirtualMachineDetailTable(VirtualMachineTable): # VM components # -class InterfaceTable(BaseTable): +class VMInterfaceTable(BaseTable): class Meta(BaseTable.Meta): model = VMInterface diff --git a/netbox/virtualization/tests/test_api.py b/netbox/virtualization/tests/test_api.py index c307d6da6..8d525f4fe 100644 --- a/netbox/virtualization/tests/test_api.py +++ b/netbox/virtualization/tests/test_api.py @@ -194,7 +194,7 @@ class VirtualMachineTest(APIViewTestCases.APIViewTestCase): # TODO: Standardize InterfaceTest (pending #4721) -class InterfaceTest(APITestCase): +class VMInterfaceTest(APITestCase): def setUp(self): diff --git a/netbox/virtualization/tests/test_filters.py b/netbox/virtualization/tests/test_filters.py index 9fe6b61d5..821127b92 100644 --- a/netbox/virtualization/tests/test_filters.py +++ b/netbox/virtualization/tests/test_filters.py @@ -367,7 +367,7 @@ class VirtualMachineTestCase(TestCase): class InterfaceTestCase(TestCase): queryset = VMInterface.objects.all() - filterset = InterfaceFilterSet + filterset = VMInterfaceFilterSet @classmethod def setUpTestData(cls): diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py index 2a8cc8ca8..772603ea1 100644 --- a/netbox/virtualization/tests/test_views.py +++ b/netbox/virtualization/tests/test_views.py @@ -191,11 +191,11 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase): # TODO: Update base class to DeviceComponentViewTestCase # Blocked by #4721 -class InterfaceTestCase( +class VMInterfaceTestCase( ViewTestCases.GetObjectViewTestCase, ViewTestCases.EditObjectViewTestCase, ViewTestCases.DeleteObjectViewTestCase, - # ViewTestCases.BulkCreateObjectsViewTestCase, + ViewTestCases.BulkCreateObjectsViewTestCase, ViewTestCases.BulkEditObjectsViewTestCase, ViewTestCases.BulkDeleteObjectsViewTestCase, ): @@ -234,7 +234,6 @@ class InterfaceTestCase( 'virtual_machine': virtualmachines[1].pk, 'name': 'Interface X', 'enabled': False, - 'mgmt_only': False, 'mac_address': EUI('01-02-03-04-05-06'), 'mtu': 2000, 'description': 'New description', @@ -248,7 +247,6 @@ class InterfaceTestCase( 'virtual_machine': virtualmachines[1].pk, 'name_pattern': 'Interface [4-6]', 'enabled': False, - 'mgmt_only': False, 'mac_address': EUI('01-02-03-04-05-06'), 'mtu': 2000, 'description': 'New description', @@ -264,6 +262,6 @@ class InterfaceTestCase( 'mtu': 2000, 'description': 'New description', 'mode': InterfaceModeChoices.MODE_TAGGED, - # 'untagged_vlan': vlans[0].pk, - # 'tagged_vlans': [v.pk for v in vlans[1:4]], + 'untagged_vlan': vlans[0].pk, + 'tagged_vlans': [v.pk for v in vlans[1:4]], } diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index e02103bc0..1fff399ed 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -291,9 +291,9 @@ class VirtualMachineBulkDeleteView(BulkDeleteView): class InterfaceListView(ObjectListView): queryset = VMInterface.objects.prefetch_related('virtual_machine', 'virtual_machine__tenant', 'cable') - filterset = filters.InterfaceFilterSet - filterset_form = forms.InterfaceFilterForm - table = tables.InterfaceTable + filterset = filters.VMInterfaceFilterSet + filterset_form = forms.VMInterfaceFilterForm + table = tables.VMInterfaceTable action_buttons = ('import', 'export') @@ -334,14 +334,14 @@ class InterfaceView(ObjectView): # TODO: This should not use ComponentCreateView class InterfaceCreateView(ComponentCreateView): queryset = VMInterface.objects.all() - form = forms.InterfaceCreateForm - model_form = forms.InterfaceForm + form = forms.VMInterfaceCreateForm + model_form = forms.VMInterfaceForm template_name = 'virtualization/virtualmachine_component_add.html' class InterfaceEditView(ObjectEditView): queryset = VMInterface.objects.all() - model_form = forms.InterfaceForm + model_form = forms.VMInterfaceForm template_name = 'virtualization/vminterface_edit.html' @@ -351,13 +351,13 @@ class InterfaceDeleteView(ObjectDeleteView): class InterfaceBulkEditView(BulkEditView): queryset = VMInterface.objects.all() - table = tables.InterfaceTable - form = forms.InterfaceBulkEditForm + table = tables.VMInterfaceTable + form = forms.VMInterfaceBulkEditForm class InterfaceBulkDeleteView(BulkDeleteView): queryset = VMInterface.objects.all() - table = tables.InterfaceTable + table = tables.VMInterfaceTable # @@ -367,9 +367,9 @@ class InterfaceBulkDeleteView(BulkDeleteView): class VirtualMachineBulkAddInterfaceView(BulkComponentCreateView): parent_model = VirtualMachine parent_field = 'virtual_machine' - form = forms.InterfaceBulkCreateForm + form = forms.VMInterfaceBulkCreateForm queryset = VMInterface.objects.all() - model_form = forms.InterfaceForm + model_form = forms.VMInterfaceForm filterset = filters.VirtualMachineFilterSet table = tables.VirtualMachineTable default_return_url = 'virtualization:virtualmachine_list'