diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 68792fd88..45fdb6825 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -2180,6 +2180,10 @@ class ConsolePortForm(BootstrapMixin, forms.ModelForm): class ConsolePortCreateForm(ComponentForm): + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) name_pattern = ExpandableNameField( label='Name' ) @@ -2238,6 +2242,10 @@ class ConsoleServerPortForm(BootstrapMixin, forms.ModelForm): class ConsoleServerPortCreateForm(ComponentForm): + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) name_pattern = ExpandableNameField( label='Name' ) @@ -2331,6 +2339,10 @@ class PowerPortForm(BootstrapMixin, forms.ModelForm): class PowerPortCreateForm(ComponentForm): + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) name_pattern = ExpandableNameField( label='Name' ) @@ -2412,6 +2424,10 @@ class PowerOutletForm(BootstrapMixin, forms.ModelForm): class PowerOutletCreateForm(ComponentForm): + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) name_pattern = ExpandableNameField( label='Name' ) @@ -2437,11 +2453,13 @@ class PowerOutletCreateForm(ComponentForm): ) def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # Limit power_port choices to those on the parent device - self.fields['power_port'].queryset = PowerPort.objects.filter(device=self.parent) + # Limit power_port queryset to PowerPorts which belong to the parent Device + device = Device.objects.get( + pk=self.initial.get('device') or self.data.get('device') + ) + self.fields['power_port'].queryset = PowerPort.objects.filter(device=device) class PowerOutletCSVForm(forms.ModelForm): @@ -2499,6 +2517,10 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): queryset=PowerOutlet.objects.all(), widget=forms.MultipleHiddenInput() ) + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) type = forms.ChoiceField( choices=add_blank_choice(PowerOutletTypeChoices), required=False @@ -2525,7 +2547,9 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): super().__init__(*args, **kwargs) # Limit power_port queryset to PowerPorts which belong to the parent Device - self.fields['power_port'].queryset = PowerPort.objects.filter(device=self.parent_obj) + if 'device' in self.initial: + device = Device.objects.filter(pk=self.initial['device']).first() + self.fields['power_port'].queryset = PowerPort.objects.filter(device=device) class PowerOutletBulkRenameForm(BulkRenameForm): @@ -2625,7 +2649,8 @@ class InterfaceCreateForm(InterfaceCommonForm, ComponentForm, forms.Form): widget=StaticSelect2(), ) enabled = forms.BooleanField( - required=False + required=False, + initial=True ) lag = forms.ModelChoiceField( queryset=Interface.objects.all(), @@ -2680,21 +2705,16 @@ class InterfaceCreateForm(InterfaceCommonForm, ComponentForm, forms.Form): ) def __init__(self, *args, **kwargs): - - # Set interfaces enabled by default - kwargs['initial'] = kwargs.get('initial', {}).copy() - kwargs['initial'].update({'enabled': True}) - super().__init__(*args, **kwargs) - # Limit LAG choices to interfaces belonging to this device (or its VC master) - if self.parent is not None: - self.fields['lag'].queryset = Interface.objects.filter( - device__in=[self.parent, self.parent.get_vc_master()], - type=InterfaceTypeChoices.TYPE_LAG - ) - else: - self.fields['lag'].queryset = Interface.objects.none() + # Limit LAG choices to interfaces which belong to the parent device (or VC master) + device = Device.objects.get( + pk=self.initial.get('device') or self.data.get('device') + ) + self.fields['lag'].queryset = Interface.objects.filter( + device__in=[device, device.get_vc_master()], + type=InterfaceTypeChoices.TYPE_LAG + ) class InterfaceCSVForm(forms.ModelForm): @@ -2769,6 +2789,10 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput() ) + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) type = forms.ChoiceField( choices=add_blank_choice(InterfaceTypeChoices), required=False, @@ -2836,14 +2860,12 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): super().__init__(*args, **kwargs) # Limit LAG choices to interfaces which belong to the parent device (or VC master) - device = self.parent_obj - if device is not None: + if 'device' in self.initial: + device = Device.objects.filter(pk=self.initial['device']).first() self.fields['lag'].queryset = Interface.objects.filter( device__in=[device, device.get_vc_master()], type=InterfaceTypeChoices.TYPE_LAG ) - else: - self.fields['lag'].choices = [] def clean(self): @@ -2909,6 +2931,10 @@ class FrontPortForm(BootstrapMixin, forms.ModelForm): # TODO: Merge with FrontPortTemplateCreateForm to remove duplicate logic class FrontPortCreateForm(ComponentForm): + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) name_pattern = ExpandableNameField( label='Name' ) @@ -2928,15 +2954,20 @@ class FrontPortCreateForm(ComponentForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - # Determine which rear port positions are occupied. These will be excluded from the list of available mappings. + device = Device.objects.get( + pk=self.initial.get('device') or self.data.get('device') + ) + + # Determine which rear port positions are occupied. These will be excluded from the list of available + # mappings. occupied_port_positions = [ (front_port.rear_port_id, front_port.rear_port_position) - for front_port in self.parent.frontports.all() + for front_port in device.frontports.all() ] # Populate rear port choices choices = [] - rear_ports = RearPort.objects.filter(device=self.parent) + rear_ports = RearPort.objects.filter(device=device) for rear_port in rear_ports: for i in range(1, rear_port.positions + 1): if (rear_port.pk, i) not in occupied_port_positions: @@ -3076,6 +3107,10 @@ class RearPortForm(BootstrapMixin, forms.ModelForm): class RearPortCreateForm(ComponentForm): + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) name_pattern = ExpandableNameField( label='Name' ) @@ -3680,6 +3715,10 @@ class DeviceBayForm(BootstrapMixin, forms.ModelForm): class DeviceBayCreateForm(ComponentForm): + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + widget=forms.HiddenInput() + ) name_pattern = ExpandableNameField( label='Name' ) diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index aa880b67d..803fee30b 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -682,11 +682,11 @@ class ConsolePortTestCase(StandardTestCases.Views): # Disable inapplicable views test_get_object = None + test_create_object = None test_bulk_edit_objects = None - # TODO - test_create_object = None - test_bulk_delete_objects = None + def test_bulk_create_objects(self): + return self._test_bulk_create_objects(expected_count=3) @classmethod def setUpTestData(cls): @@ -704,11 +704,14 @@ class ConsolePortTestCase(StandardTestCases.Views): 'type': ConsolePortTypeChoices.TYPE_RJ45, 'description': 'A console port', 'tags': 'Alpha,Bravo,Charlie', + } - # Extraneous model fields - 'cable': None, - 'connected_endpoint': None, - 'connection_status': None, + cls.bulk_create_data = { + 'device': device.pk, + 'name_pattern': 'Console Port [4-6]', + 'type': ConsolePortTypeChoices.TYPE_RJ45, + 'description': 'A console port', + 'tags': 'Alpha,Bravo,Charlie', } cls.csv_data = ( @@ -724,11 +727,10 @@ class ConsoleServerPortTestCase(StandardTestCases.Views): # Disable inapplicable views test_get_object = None - - # TODO test_create_object = None - test_bulk_edit_objects = None - test_bulk_delete_objects = None + + def test_bulk_create_objects(self): + return self._test_bulk_create_objects(expected_count=3) @classmethod def setUpTestData(cls): @@ -746,10 +748,20 @@ class ConsoleServerPortTestCase(StandardTestCases.Views): 'type': ConsolePortTypeChoices.TYPE_RJ45, 'description': 'A console server port', 'tags': 'Alpha,Bravo,Charlie', + } - # Extraneous model fields - 'cable': None, - 'connection_status': None, + cls.bulk_create_data = { + 'device': device.pk, + 'name_pattern': 'Console Server Port [4-6]', + 'type': ConsolePortTypeChoices.TYPE_RJ45, + 'description': 'A console server port', + 'tags': 'Alpha,Bravo,Charlie', + } + + cls.bulk_edit_data = { + 'device': device.pk, + 'type': ConsolePortTypeChoices.TYPE_RJ45, + 'description': 'New description', } cls.csv_data = ( @@ -766,10 +778,10 @@ class PowerPortTestCase(StandardTestCases.Views): # Disable inapplicable views test_get_object = None test_bulk_edit_objects = None - - # TODO test_create_object = None - test_bulk_delete_objects = None + + def test_bulk_create_objects(self): + return self._test_bulk_create_objects(expected_count=3) @classmethod def setUpTestData(cls): @@ -789,10 +801,16 @@ class PowerPortTestCase(StandardTestCases.Views): 'allocated_draw': 50, 'description': 'A power port', 'tags': 'Alpha,Bravo,Charlie', + } - # Extraneous model fields - 'cable': None, - 'connection_status': None, + cls.bulk_create_data = { + 'device': device.pk, + 'name_pattern': 'Power Port [4-6]]', + 'type': PowerPortTypeChoices.TYPE_IEC_C14, + 'maximum_draw': 100, + 'allocated_draw': 50, + 'description': 'A power port', + 'tags': 'Alpha,Bravo,Charlie', } cls.csv_data = ( @@ -808,11 +826,10 @@ class PowerOutletTestCase(StandardTestCases.Views): # Disable inapplicable views test_get_object = None - - # TODO test_create_object = None - test_bulk_edit_objects = None - test_bulk_delete_objects = None + + def test_bulk_create_objects(self): + return self._test_bulk_create_objects(expected_count=3) @classmethod def setUpTestData(cls): @@ -838,10 +855,24 @@ class PowerOutletTestCase(StandardTestCases.Views): 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B, 'description': 'A power outlet', 'tags': 'Alpha,Bravo,Charlie', + } - # Extraneous model fields - 'cable': None, - 'connection_status': None, + cls.bulk_create_data = { + 'device': device.pk, + 'name_pattern': 'Power Outlet [4-6]', + 'type': PowerOutletTypeChoices.TYPE_IEC_C13, + 'power_port': powerports[1].pk, + 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B, + 'description': 'A power outlet', + 'tags': 'Alpha,Bravo,Charlie', + } + + cls.bulk_edit_data = { + 'device': device.pk, + 'type': PowerOutletTypeChoices.TYPE_IEC_C13, + 'power_port': powerports[1].pk, + 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B, + 'description': 'New description', } cls.csv_data = ( @@ -855,20 +886,23 @@ class PowerOutletTestCase(StandardTestCases.Views): class InterfaceTestCase(StandardTestCases.Views): model = Interface - # TODO + # Disable inapplicable views test_create_object = None - test_bulk_edit_objects = None - test_bulk_delete_objects = None + + def test_bulk_create_objects(self): + return self._test_bulk_create_objects(expected_count=3) @classmethod def setUpTestData(cls): device = create_test_device('Device 1') - Interface.objects.bulk_create([ + interfaces = ( Interface(device=device, name='Interface 1'), Interface(device=device, name='Interface 2'), Interface(device=device, name='Interface 3'), - ]) + Interface(device=device, name='LAG', type=InterfaceTypeChoices.TYPE_LAG), + ) + Interface.objects.bulk_create(interfaces) vlans = ( VLAN(vid=1, name='VLAN1', site=device.site), @@ -884,7 +918,38 @@ class InterfaceTestCase(StandardTestCases.Views): 'name': 'Interface X', 'type': InterfaceTypeChoices.TYPE_1GE_GBIC, 'enabled': False, - 'lag': None, + 'lag': interfaces[3].pk, + 'mac_address': EUI('01:02:03:04:05:06'), + 'mtu': 2000, + 'mgmt_only': True, + 'description': 'A front port', + 'mode': InterfaceModeChoices.MODE_TAGGED, + 'untagged_vlan': vlans[0].pk, + 'tagged_vlans': [v.pk for v in vlans[1:4]], + 'tags': 'Alpha,Bravo,Charlie', + } + + cls.bulk_create_data = { + 'device': device.pk, + 'name_pattern': 'Interface [4-6]', + 'type': InterfaceTypeChoices.TYPE_1GE_GBIC, + 'enabled': False, + 'lag': interfaces[3].pk, + 'mac_address': EUI('01:02:03:04:05:06'), + 'mtu': 2000, + 'mgmt_only': True, + 'description': 'A front port', + 'mode': InterfaceModeChoices.MODE_TAGGED, + 'untagged_vlan': vlans[0].pk, + 'tagged_vlans': [v.pk for v in vlans[1:4]], + 'tags': 'Alpha,Bravo,Charlie', + } + + cls.bulk_edit_data = { + 'device': device.pk, + 'type': InterfaceTypeChoices.TYPE_1GE_GBIC, + 'enabled': False, + 'lag': interfaces[3].pk, 'mac_address': EUI('01:02:03:04:05:06'), 'mtu': 2000, 'mgmt_only': True, @@ -892,11 +957,6 @@ class InterfaceTestCase(StandardTestCases.Views): 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': vlans[0].pk, 'tagged_vlans': [v.pk for v in vlans[1:4]], - 'tags': 'Alpha,Bravo,Charlie', - - # Extraneous model fields - 'cable': None, - 'connection_status': None, } cls.csv_data = ( @@ -912,11 +972,10 @@ class FrontPortTestCase(StandardTestCases.Views): # Disable inapplicable views test_get_object = None - - # TODO test_create_object = None - test_bulk_edit_objects = None - test_bulk_delete_objects = None + + def test_bulk_create_objects(self): + return self._test_bulk_create_objects(expected_count=3) @classmethod def setUpTestData(cls): @@ -946,9 +1005,22 @@ class FrontPortTestCase(StandardTestCases.Views): 'rear_port_position': 1, 'description': 'New description', 'tags': 'Alpha,Bravo,Charlie', + } - # Extraneous model fields - 'cable': None, + cls.bulk_create_data = { + 'device': device.pk, + 'name_pattern': 'Front Port [4-6]', + 'type': PortTypeChoices.TYPE_8P8C, + 'rear_port_set': [ + '{}:1'.format(rp.pk) for rp in rearports[3:6] + ], + 'description': 'New description', + 'tags': 'Alpha,Bravo,Charlie', + } + + cls.bulk_edit_data = { + 'type': PortTypeChoices.TYPE_8P8C, + 'description': 'New description', } cls.csv_data = ( @@ -964,11 +1036,10 @@ class RearPortTestCase(StandardTestCases.Views): # Disable inapplicable views test_get_object = None - - # TODO test_create_object = None - test_bulk_edit_objects = None - test_bulk_delete_objects = None + + def test_bulk_create_objects(self): + return self._test_bulk_create_objects(expected_count=3) @classmethod def setUpTestData(cls): @@ -985,11 +1056,22 @@ class RearPortTestCase(StandardTestCases.Views): 'name': 'Rear Port X', 'type': PortTypeChoices.TYPE_8P8C, 'positions': 3, - 'description': 'New description', + 'description': 'A rear port', 'tags': 'Alpha,Bravo,Charlie', + } - # Extraneous model fields - 'cable': None, + cls.bulk_create_data = { + 'device': device.pk, + 'name_pattern': 'Rear Port [4-6]', + 'type': PortTypeChoices.TYPE_8P8C, + 'positions': 3, + 'description': 'A rear port', + 'tags': 'Alpha,Bravo,Charlie', + } + + cls.bulk_edit_data = { + 'type': PortTypeChoices.TYPE_8P8C, + 'description': 'New description', } cls.csv_data = ( @@ -1005,11 +1087,13 @@ class DeviceBayTestCase(StandardTestCases.Views): # Disable inapplicable views test_get_object = None + test_create_object = None # TODO - test_create_object = None test_bulk_edit_objects = None - test_bulk_delete_objects = None + + def test_bulk_create_objects(self): + return self._test_bulk_create_objects(expected_count=3) @classmethod def setUpTestData(cls): @@ -1030,9 +1114,13 @@ class DeviceBayTestCase(StandardTestCases.Views): 'name': 'Device Bay X', 'description': 'A device bay', 'tags': 'Alpha,Bravo,Charlie', + } - # Extraneous model fields - 'installed_device': None, + cls.bulk_create_data = { + 'device': device2.pk, + 'name_pattern': 'Device Bay [4-6]', + 'description': 'A device bay', + 'tags': 'Alpha,Bravo,Charlie', } cls.csv_data = ( @@ -1076,13 +1164,6 @@ class InventoryItemTestCase(StandardTestCases.Views): 'tags': 'Alpha,Bravo,Charlie', } - cls.csv_data = ( - "device,name", - "Device 1,Inventory Item 4", - "Device 1,Inventory Item 5", - "Device 1,Inventory Item 6", - ) - cls.bulk_edit_data = { 'device': device.pk, 'manufacturer': manufacturer.pk, @@ -1090,6 +1171,13 @@ class InventoryItemTestCase(StandardTestCases.Views): 'description': 'New description', } + cls.csv_data = ( + "device,name", + "Device 1,Inventory Item 4", + "Device 1,Inventory Item 5", + "Device 1,Inventory Item 6", + ) + class CableTestCase(StandardTestCases.Views): model = Cable diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 38e24308b..121beff97 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -169,109 +169,112 @@ urlpatterns = [ # Console ports path(r'devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'), - path(r'devices//console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'), - path(r'devices//console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'), path(r'console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'), + path(r'console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'), + path(r'console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'), + # TODO: Bulk edit view for ConsolePorts + path(r'console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'), path(r'console-ports//connect//', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}), path(r'console-ports//edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'), path(r'console-ports//delete/', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'), path(r'console-ports//trace/', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}), - path(r'console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'), # Console server ports path(r'devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'), - path(r'devices//console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'), - path(r'devices//console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'), - path(r'devices//console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'), path(r'console-server-ports/', views.ConsoleServerPortListView.as_view(), name='consoleserverport_list'), + path(r'console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'), + path(r'console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'), + path(r'console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'), + path(r'console-server-ports/import/', views.ConsoleServerPortBulkImportView.as_view(), name='consoleserverport_import'), + path(r'console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'), + path(r'console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'), path(r'console-server-ports//connect//', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}), path(r'console-server-ports//edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'), path(r'console-server-ports//delete/', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'), path(r'console-server-ports//trace/', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}), - path(r'console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'), - path(r'console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'), - path(r'console-server-ports/import/', views.ConsoleServerPortBulkImportView.as_view(), name='consoleserverport_import'), # Power ports path(r'devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'), - path(r'devices//power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'), - path(r'devices//power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'), path(r'power-ports/', views.PowerPortListView.as_view(), name='powerport_list'), + path(r'power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'), + path(r'power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'), + # TODO: Bulk edit view for PowerPorts + path(r'power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'), path(r'power-ports//connect//', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}), path(r'power-ports//edit/', views.PowerPortEditView.as_view(), name='powerport_edit'), path(r'power-ports//delete/', views.PowerPortDeleteView.as_view(), name='powerport_delete'), path(r'power-ports//trace/', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}), - path(r'power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'), # Power outlets path(r'devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'), - path(r'devices//power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'), - path(r'devices//power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'), - path(r'devices//power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'), path(r'power-outlets/', views.PowerOutletListView.as_view(), name='poweroutlet_list'), + path(r'power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'), + path(r'power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'), + path(r'power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'), + path(r'power-outlets/import/', views.PowerOutletBulkImportView.as_view(), name='poweroutlet_import'), + path(r'power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'), + path(r'power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'), path(r'power-outlets//connect//', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}), path(r'power-outlets//edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'), path(r'power-outlets//delete/', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'), path(r'power-outlets//trace/', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}), - path(r'power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'), - path(r'power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'), - path(r'power-outlets/import/', views.PowerOutletBulkImportView.as_view(), name='poweroutlet_import'), # Interfaces path(r'devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'), - path(r'devices//interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'), - path(r'devices//interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), - path(r'devices//interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), path(r'interfaces/', views.InterfaceListView.as_view(), name='interface_list'), + path(r'interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'), + path(r'interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'), + path(r'interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'), + path(r'interfaces/import/', views.InterfaceBulkImportView.as_view(), name='interface_import'), + path(r'interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), + path(r'interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), path(r'interfaces//connect//', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}), path(r'interfaces//', views.InterfaceView.as_view(), name='interface'), path(r'interfaces//edit/', views.InterfaceEditView.as_view(), name='interface_edit'), path(r'interfaces//delete/', views.InterfaceDeleteView.as_view(), name='interface_delete'), path(r'interfaces//changelog/', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}), path(r'interfaces//trace/', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}), - path(r'interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'), - path(r'interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'), - path(r'interfaces/import/', views.InterfaceBulkImportView.as_view(), name='interface_import'), # Front ports # path(r'devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'), - path(r'devices//front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'), - path(r'devices//front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'), - path(r'devices//front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'), path(r'front-ports/', views.FrontPortListView.as_view(), name='frontport_list'), + path(r'front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'), + path(r'front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'), + path(r'front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'), + path(r'front-ports/import/', views.FrontPortBulkImportView.as_view(), name='frontport_import'), + path(r'front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'), + path(r'front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'), path(r'front-ports//connect//', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}), path(r'front-ports//edit/', views.FrontPortEditView.as_view(), name='frontport_edit'), path(r'front-ports//delete/', views.FrontPortDeleteView.as_view(), name='frontport_delete'), path(r'front-ports//trace/', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}), - path(r'front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'), - path(r'front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'), - path(r'front-ports/import/', views.FrontPortBulkImportView.as_view(), name='frontport_import'), # Rear ports # path(r'devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'), - path(r'devices//rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'), - path(r'devices//rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'), - path(r'devices//rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'), path(r'rear-ports/', views.RearPortListView.as_view(), name='rearport_list'), + path(r'rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'), + path(r'rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'), + path(r'rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'), + path(r'rear-ports/import/', views.RearPortBulkImportView.as_view(), name='rearport_import'), + path(r'rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'), + path(r'rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'), path(r'rear-ports//connect//', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}), path(r'rear-ports//edit/', views.RearPortEditView.as_view(), name='rearport_edit'), path(r'rear-ports//delete/', views.RearPortDeleteView.as_view(), name='rearport_delete'), path(r'rear-ports//trace/', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}), - path(r'rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'), - path(r'rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'), - path(r'rear-ports/import/', views.RearPortBulkImportView.as_view(), name='rearport_import'), # Device bays path(r'devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'), - path(r'devices//bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'), - path(r'devices//bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'), path(r'device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'), + path(r'device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'), + path(r'device-bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'), + path(r'device-bays/import/', views.DeviceBayBulkImportView.as_view(), name='devicebay_import'), + # TODO: Bulk edit view for DeviceBays + path(r'device-bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'), path(r'device-bays//edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'), path(r'device-bays//delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'), path(r'device-bays//populate/', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'), path(r'device-bays//depopulate/', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'), - path(r'device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'), - path(r'device-bays/import/', views.DeviceBayBulkImportView.as_view(), name='devicebay_import'), # Inventory items path(r'inventory-items/', views.InventoryItemListView.as_view(), name='inventoryitem_list'), @@ -280,6 +283,7 @@ urlpatterns = [ path(r'inventory-items/delete/', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'), path(r'inventory-items//edit/', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'), path(r'inventory-items//delete/', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'), + # TODO: Replace below with InventoryItemCreateView path(r'devices//inventory-items/add/', views.InventoryItemEditView.as_view(), name='inventoryitem_add'), # Cables diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index fd3d09ab7..6e7018fb5 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1205,8 +1205,6 @@ class ConsolePortListView(PermissionRequiredMixin, ObjectListView): class ConsolePortCreateView(PermissionRequiredMixin, ComponentCreateView): permission_required = 'dcim.add_consoleport' - parent_model = Device - parent_field = 'device' model = ConsolePort form = forms.ConsolePortCreateForm model_form = forms.ConsolePortForm @@ -1234,8 +1232,8 @@ class ConsolePortBulkImportView(PermissionRequiredMixin, BulkImportView): class ConsolePortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_consoleport' queryset = ConsolePort.objects.all() - parent_model = Device table = tables.ConsolePortTable + default_return_url = 'dcim:consoleport_list' # @@ -1253,8 +1251,6 @@ class ConsoleServerPortListView(PermissionRequiredMixin, ObjectListView): class ConsoleServerPortCreateView(PermissionRequiredMixin, ComponentCreateView): permission_required = 'dcim.add_consoleserverport' - parent_model = Device - parent_field = 'device' model = ConsoleServerPort form = forms.ConsoleServerPortCreateForm model_form = forms.ConsoleServerPortForm @@ -1282,7 +1278,6 @@ class ConsoleServerPortBulkImportView(PermissionRequiredMixin, BulkImportView): class ConsoleServerPortBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_consoleserverport' queryset = ConsoleServerPort.objects.all() - parent_model = Device table = tables.ConsoleServerPortTable form = forms.ConsoleServerPortBulkEditForm @@ -1302,8 +1297,8 @@ class ConsoleServerPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnec class ConsoleServerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_consoleserverport' queryset = ConsoleServerPort.objects.all() - parent_model = Device table = tables.ConsoleServerPortTable + default_return_url = 'dcim:consoleserverport_list' # @@ -1321,8 +1316,6 @@ class PowerPortListView(PermissionRequiredMixin, ObjectListView): class PowerPortCreateView(PermissionRequiredMixin, ComponentCreateView): permission_required = 'dcim.add_powerport' - parent_model = Device - parent_field = 'device' model = PowerPort form = forms.PowerPortCreateForm model_form = forms.PowerPortForm @@ -1350,8 +1343,8 @@ class PowerPortBulkImportView(PermissionRequiredMixin, BulkImportView): class PowerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_powerport' queryset = PowerPort.objects.all() - parent_model = Device table = tables.PowerPortTable + default_return_url = 'dcim:powerport_list' # @@ -1369,8 +1362,6 @@ class PowerOutletListView(PermissionRequiredMixin, ObjectListView): class PowerOutletCreateView(PermissionRequiredMixin, ComponentCreateView): permission_required = 'dcim.add_poweroutlet' - parent_model = Device - parent_field = 'device' model = PowerOutlet form = forms.PowerOutletCreateForm model_form = forms.PowerOutletForm @@ -1398,7 +1389,6 @@ class PowerOutletBulkImportView(PermissionRequiredMixin, BulkImportView): class PowerOutletBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_poweroutlet' queryset = PowerOutlet.objects.all() - parent_model = Device table = tables.PowerOutletTable form = forms.PowerOutletBulkEditForm @@ -1418,8 +1408,8 @@ class PowerOutletBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView) class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_poweroutlet' queryset = PowerOutlet.objects.all() - parent_model = Device table = tables.PowerOutletTable + default_return_url = 'dcim:poweroutlet_list' # @@ -1473,8 +1463,6 @@ class InterfaceView(PermissionRequiredMixin, View): class InterfaceCreateView(PermissionRequiredMixin, ComponentCreateView): permission_required = 'dcim.add_interface' - parent_model = Device - parent_field = 'device' model = Interface form = forms.InterfaceCreateForm model_form = forms.InterfaceForm @@ -1503,7 +1491,6 @@ class InterfaceBulkImportView(PermissionRequiredMixin, BulkImportView): class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_interface' queryset = Interface.objects.all() - parent_model = Device table = tables.InterfaceTable form = forms.InterfaceBulkEditForm @@ -1523,8 +1510,8 @@ class InterfaceBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView): class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_interface' queryset = Interface.objects.all() - parent_model = Device table = tables.InterfaceTable + default_return_url = 'dcim:interface_list' # @@ -1542,8 +1529,6 @@ class FrontPortListView(PermissionRequiredMixin, ObjectListView): class FrontPortCreateView(PermissionRequiredMixin, ComponentCreateView): permission_required = 'dcim.add_frontport' - parent_model = Device - parent_field = 'device' model = FrontPort form = forms.FrontPortCreateForm model_form = forms.FrontPortForm @@ -1571,7 +1556,6 @@ class FrontPortBulkImportView(PermissionRequiredMixin, BulkImportView): class FrontPortBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_frontport' queryset = FrontPort.objects.all() - parent_model = Device table = tables.FrontPortTable form = forms.FrontPortBulkEditForm @@ -1591,8 +1575,8 @@ class FrontPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView): class FrontPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_frontport' queryset = FrontPort.objects.all() - parent_model = Device table = tables.FrontPortTable + default_return_url = 'dcim:frontport_list' # @@ -1610,8 +1594,6 @@ class RearPortListView(PermissionRequiredMixin, ObjectListView): class RearPortCreateView(PermissionRequiredMixin, ComponentCreateView): permission_required = 'dcim.add_rearport' - parent_model = Device - parent_field = 'device' model = RearPort form = forms.RearPortCreateForm model_form = forms.RearPortForm @@ -1639,7 +1621,6 @@ class RearPortBulkImportView(PermissionRequiredMixin, BulkImportView): class RearPortBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_rearport' queryset = RearPort.objects.all() - parent_model = Device table = tables.RearPortTable form = forms.RearPortBulkEditForm @@ -1659,8 +1640,8 @@ class RearPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView): class RearPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_rearport' queryset = RearPort.objects.all() - parent_model = Device table = tables.RearPortTable + default_return_url = 'dcim:rearport_list' # @@ -1680,8 +1661,6 @@ class DeviceBayListView(PermissionRequiredMixin, ObjectListView): class DeviceBayCreateView(PermissionRequiredMixin, ComponentCreateView): permission_required = 'dcim.add_devicebay' - parent_model = Device - parent_field = 'device' model = DeviceBay form = forms.DeviceBayCreateForm model_form = forms.DeviceBayForm @@ -1784,8 +1763,8 @@ class DeviceBayBulkRenameView(PermissionRequiredMixin, BulkRenameView): class DeviceBayBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_devicebay' queryset = DeviceBay.objects.all() - parent_model = Device table = tables.DeviceBayTable + default_return_url = 'dcim:devicebay_list' # diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index fa37f1ac5..5ede19d78 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -48,14 +48,30 @@ Add Components {% endif %} @@ -333,12 +349,12 @@ {% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}