diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 1f4138b00..4e7ddbe5f 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -549,7 +549,7 @@ class DeviceForm(BootstrapMixin, CustomFieldForm): if pk and self.instance.device_type.is_child_device and hasattr(self.instance, 'parent_bay'): self.fields['site'].disabled = True self.fields['rack'].disabled = True - self.initial['site'] = self.instance.parent_bay.device.rack.site_id + self.initial['site'] = self.instance.parent_bay.device.site_id self.initial['rack'] = self.instance.parent_bay.device.rack_id @@ -748,9 +748,13 @@ class ConsolePortCreateForm(BootstrapMixin, forms.Form): class ConsoleConnectionCSVForm(forms.Form): - console_server = FlexibleModelChoiceField(queryset=Device.objects.filter(device_type__is_console_server=True), - to_field_name='name', - error_messages={'invalid_choice': 'Console server not found'}) + console_server = FlexibleModelChoiceField( + queryset=Device.objects.filter(device_type__is_console_server=True), + to_field_name='name', + error_messages={ + 'invalid_choice': 'Console server not found', + } + ) cs_port = forms.CharField() device = FlexibleModelChoiceField(queryset=Device.objects.all(), to_field_name='name', error_messages={'invalid_choice': 'Device not found'}) @@ -815,22 +819,49 @@ class ConsoleConnectionImportForm(BootstrapMixin, BulkImportForm): class ConsolePortConnectionForm(BootstrapMixin, forms.ModelForm): - rack = forms.ModelChoiceField(queryset=Rack.objects.all(), label='Rack', required=False, - widget=forms.Select(attrs={'filter-for': 'console_server'})) - console_server = forms.ModelChoiceField(queryset=Device.objects.all(), label='Console Server', required=False, - widget=APISelect(api_url='/api/dcim/devices/?rack_id={{rack}}&is_console_server=True', - display_field='display_name', - attrs={'filter-for': 'cs_port'})) - livesearch = forms.CharField(required=False, label='Console Server', widget=Livesearch( - query_key='q', query_url='dcim-api:device_list', field_to_update='console_server') + site = forms.ModelChoiceField( + queryset=Site.objects.all(), + widget=forms.HiddenInput(), + ) + rack = forms.ModelChoiceField( + queryset=Rack.objects.all(), + label='Rack', + required=False, + widget=forms.Select( + attrs={'filter-for': 'console_server', 'nullable': 'true'} + ) + ) + console_server = forms.ModelChoiceField( + queryset=Device.objects.all(), + label='Console Server', + required=False, + widget=APISelect( + api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}&is_console_server=True', + display_field='display_name', + attrs={'filter-for': 'cs_port'} + ) + ) + livesearch = forms.CharField( + required=False, + label='Console Server', + widget=Livesearch( + query_key='q', + query_url='dcim-api:device_list', + field_to_update='console_server', + ) + ) + cs_port = forms.ModelChoiceField( + queryset=ConsoleServerPort.objects.all(), + label='Port', + widget=APISelect( + api_url='/api/dcim/devices/{{console_server}}/console-server-ports/', + disabled_indicator='connected_console', + ) ) - cs_port = forms.ModelChoiceField(queryset=ConsoleServerPort.objects.all(), label='Port', - widget=APISelect(api_url='/api/dcim/devices/{{console_server}}/console-server-ports/', - disabled_indicator='connected_console')) class Meta: model = ConsolePort - fields = ['rack', 'console_server', 'livesearch', 'cs_port', 'connection_status'] + fields = ['site', 'rack', 'console_server', 'livesearch', 'cs_port', 'connection_status'] labels = { 'cs_port': 'Port', 'connection_status': 'Status', @@ -843,17 +874,22 @@ class ConsolePortConnectionForm(BootstrapMixin, forms.ModelForm): if not self.instance.pk: raise RuntimeError("ConsolePortConnectionForm must be initialized with an existing ConsolePort instance.") - self.fields['rack'].queryset = Rack.objects.filter(site=self.instance.device.rack.site) + self.initial['site'] = self.instance.device.site + self.fields['rack'].queryset = Rack.objects.filter(site=self.instance.device.site) self.fields['cs_port'].required = True self.fields['connection_status'].choices = CONNECTION_STATUS_CHOICES # Initialize console server choices if self.is_bound and self.data.get('rack'): - self.fields['console_server'].queryset = Device.objects.filter(rack=self.data['rack'], device_type__is_console_server=True) + self.fields['console_server'].queryset = Device.objects.filter(rack=self.data['rack'], + device_type__is_console_server=True) elif self.initial.get('rack'): - self.fields['console_server'].queryset = Device.objects.filter(rack=self.initial['rack'], device_type__is_console_server=True) + self.fields['console_server'].queryset = Device.objects.filter(rack=self.initial['rack'], + device_type__is_console_server=True) else: - self.fields['console_server'].choices = [] + self.fields['console_server'].queryset = Device.objects.filter(site=self.instance.device.site, + rack__isnull=True, + device_type__is_console_server=True) # Initialize CS port choices if self.is_bound: @@ -883,10 +919,20 @@ class ConsoleServerPortCreateForm(BootstrapMixin, forms.Form): class ConsoleServerPortConnectionForm(BootstrapMixin, forms.Form): - rack = forms.ModelChoiceField(queryset=Rack.objects.all(), label='Rack', required=False, - widget=forms.Select(attrs={'filter-for': 'device'})) + site = forms.ModelChoiceField( + queryset=Site.objects.all(), + widget=forms.HiddenInput(), + ) + rack = forms.ModelChoiceField( + queryset=Rack.objects.all(), + label='Rack', + required=False, + widget=forms.Select( + attrs={'filter-for': 'device', 'nullable': 'true'} + ) + ) device = forms.ModelChoiceField(queryset=Device.objects.all(), label='Device', required=False, - widget=APISelect(api_url='/api/dcim/devices/?rack_id={{rack}}', + widget=APISelect(api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}', display_field='display_name', attrs={'filter-for': 'port'})) livesearch = forms.CharField(required=False, label='Device', widget=Livesearch( query_key='q', query_url='dcim-api:device_list', field_to_update='device') @@ -898,7 +944,7 @@ class ConsoleServerPortConnectionForm(BootstrapMixin, forms.Form): widget=forms.Select(choices=CONNECTION_STATUS_CHOICES)) class Meta: - fields = ['rack', 'device', 'livesearch', 'port', 'connection_status'] + fields = ['site', 'rack', 'device', 'livesearch', 'port', 'connection_status'] labels = { 'connection_status': 'Status', } @@ -907,7 +953,8 @@ class ConsoleServerPortConnectionForm(BootstrapMixin, forms.Form): super(ConsoleServerPortConnectionForm, self).__init__(*args, **kwargs) - self.fields['rack'].queryset = Rack.objects.filter(site=consoleserverport.device.rack.site) + self.initial['site'] = consoleserverport.device.site + self.fields['rack'].queryset = Rack.objects.filter(site=consoleserverport.device.site) # Initialize device choices if self.is_bound and self.data.get('rack'): @@ -915,7 +962,8 @@ class ConsoleServerPortConnectionForm(BootstrapMixin, forms.Form): elif self.initial.get('rack', None): self.fields['device'].queryset = Device.objects.filter(rack=self.initial['rack']) else: - self.fields['device'].choices = [] + self.fields['device'].queryset = Device.objects.filter(site=consoleserverport.device.site, + rack__isnull=True) # Initialize port choices if self.is_bound: @@ -945,8 +993,13 @@ class PowerPortCreateForm(BootstrapMixin, forms.Form): class PowerConnectionCSVForm(forms.Form): - pdu = FlexibleModelChoiceField(queryset=Device.objects.filter(device_type__is_pdu=True), to_field_name='name', - error_messages={'invalid_choice': 'PDU not found.'}) + pdu = FlexibleModelChoiceField( + queryset=Device.objects.filter(device_type__is_pdu=True), + to_field_name='name', + error_messages={ + 'invalid_choice': 'PDU not found.', + } + ) power_outlet = forms.CharField() device = FlexibleModelChoiceField(queryset=Device.objects.all(), to_field_name='name', error_messages={'invalid_choice': 'Device not found'}) @@ -1012,21 +1065,46 @@ class PowerConnectionImportForm(BootstrapMixin, BulkImportForm): class PowerPortConnectionForm(BootstrapMixin, forms.ModelForm): - rack = forms.ModelChoiceField(queryset=Rack.objects.all(), label='Rack', required=False, - widget=forms.Select(attrs={'filter-for': 'pdu'})) - pdu = forms.ModelChoiceField(queryset=Device.objects.all(), label='PDU', required=False, - widget=APISelect(api_url='/api/dcim/devices/?rack_id={{rack}}&is_pdu=True', - display_field='display_name', attrs={'filter-for': 'power_outlet'})) - livesearch = forms.CharField(required=False, label='PDU', widget=Livesearch( - query_key='q', query_url='dcim-api:device_list', field_to_update='pdu') + site = forms.ModelChoiceField(queryset=Site.objects.all(), widget=forms.HiddenInput()) + rack = forms.ModelChoiceField( + queryset=Rack.objects.all(), + label='Rack', + required=False, + widget=forms.Select( + attrs={'filter-for': 'pdu', 'nullable': 'true'} + ) + ) + pdu = forms.ModelChoiceField( + queryset=Device.objects.all(), + label='PDU', + required=False, + widget=APISelect( + api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}&is_pdu=True', + display_field='display_name', + attrs={'filter-for': 'power_outlet'} + ) + ) + livesearch = forms.CharField( + required=False, + label='PDU', + widget=Livesearch( + query_key='q', + query_url='dcim-api:device_list', + field_to_update='pdu' + ) + ) + power_outlet = forms.ModelChoiceField( + queryset=PowerOutlet.objects.all(), + label='Outlet', + widget=APISelect( + api_url='/api/dcim/devices/{{pdu}}/power-outlets/', + disabled_indicator='connected_port' + ) ) - power_outlet = forms.ModelChoiceField(queryset=PowerOutlet.objects.all(), label='Outlet', - widget=APISelect(api_url='/api/dcim/devices/{{pdu}}/power-outlets/', - disabled_indicator='connected_port')) class Meta: model = PowerPort - fields = ['rack', 'pdu', 'livesearch', 'power_outlet', 'connection_status'] + fields = ['site', 'rack', 'pdu', 'livesearch', 'power_outlet', 'connection_status'] labels = { 'power_outlet': 'Outlet', 'connection_status': 'Status', @@ -1039,17 +1117,22 @@ class PowerPortConnectionForm(BootstrapMixin, forms.ModelForm): if not self.instance.pk: raise RuntimeError("PowerPortConnectionForm must be initialized with an existing PowerPort instance.") - self.fields['rack'].queryset = Rack.objects.filter(site=self.instance.device.rack.site) + self.initial['site'] = self.instance.device.site + self.fields['rack'].queryset = Rack.objects.filter(site=self.instance.device.site) self.fields['power_outlet'].required = True self.fields['connection_status'].choices = CONNECTION_STATUS_CHOICES # Initialize PDU choices if self.is_bound and self.data.get('rack'): - self.fields['pdu'].queryset = Device.objects.filter(rack=self.data['rack'], device_type__is_pdu=True) + self.fields['pdu'].queryset = Device.objects.filter(rack=self.data['rack'], + device_type__is_pdu=True) elif self.initial.get('rack', None): - self.fields['pdu'].queryset = Device.objects.filter(rack=self.initial['rack'], device_type__is_pdu=True) + self.fields['pdu'].queryset = Device.objects.filter(rack=self.initial['rack'], + device_type__is_pdu=True) else: - self.fields['pdu'].choices = [] + self.fields['pdu'].queryset = Device.objects.filter(site=self.instance.device.site, + rack__isnull=True, + device_type__is_pdu=True) # Initialize power outlet choices if self.is_bound: @@ -1079,22 +1162,56 @@ class PowerOutletCreateForm(BootstrapMixin, forms.Form): class PowerOutletConnectionForm(BootstrapMixin, forms.Form): - rack = forms.ModelChoiceField(queryset=Rack.objects.all(), label='Rack', required=False, - widget=forms.Select(attrs={'filter-for': 'device'})) - device = forms.ModelChoiceField(queryset=Device.objects.all(), label='Device', required=False, - widget=APISelect(api_url='/api/dcim/devices/?rack_id={{rack}}', - display_field='display_name', attrs={'filter-for': 'port'})) - livesearch = forms.CharField(required=False, label='Device', widget=Livesearch( - query_key='q', query_url='dcim-api:device_list', field_to_update='device') + site = forms.ModelChoiceField( + queryset=Site.objects.all(), + widget=forms.HiddenInput() + ) + rack = forms.ModelChoiceField( + queryset=Rack.objects.all(), + label='Rack', + required=False, + widget=forms.Select( + attrs={'filter-for': 'device', 'nullable': 'true'} + ) + ) + device = forms.ModelChoiceField( + queryset=Device.objects.all(), + label='Device', + required=False, + widget=APISelect( + api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}', + display_field='display_name', + attrs={'filter-for': 'port'} + ) + ) + livesearch = forms.CharField( + required=False, + label='Device', + widget=Livesearch( + query_key='q', + query_url='dcim-api:device_list', + field_to_update='device' + ) + ) + port = forms.ModelChoiceField( + queryset=PowerPort.objects.all(), + label='Port', + widget=APISelect( + api_url='/api/dcim/devices/{{device}}/power-ports/', + disabled_indicator='power_outlet' + ) + ) + connection_status = forms.BooleanField( + required=False, + initial=CONNECTION_STATUS_CONNECTED, + label='Status', + widget=forms.Select( + choices=CONNECTION_STATUS_CHOICES + ) ) - port = forms.ModelChoiceField(queryset=PowerPort.objects.all(), label='Port', - widget=APISelect(api_url='/api/dcim/devices/{{device}}/power-ports/', - disabled_indicator='power_outlet')) - connection_status = forms.BooleanField(required=False, initial=CONNECTION_STATUS_CONNECTED, label='Status', - widget=forms.Select(choices=CONNECTION_STATUS_CHOICES)) class Meta: - fields = ['rack', 'device', 'livesearch', 'port', 'connection_status'] + fields = ['site', 'rack', 'device', 'livesearch', 'port', 'connection_status'] labels = { 'connection_status': 'Status', } @@ -1103,7 +1220,8 @@ class PowerOutletConnectionForm(BootstrapMixin, forms.Form): super(PowerOutletConnectionForm, self).__init__(*args, **kwargs) - self.fields['rack'].queryset = Rack.objects.filter(site=poweroutlet.device.rack.site) + self.initial['site'] = poweroutlet.device.site + self.fields['rack'].queryset = Rack.objects.filter(site=poweroutlet.device.site) # Initialize device choices if self.is_bound and self.data.get('rack'): @@ -1111,7 +1229,8 @@ class PowerOutletConnectionForm(BootstrapMixin, forms.Form): elif self.initial.get('rack', None): self.fields['device'].queryset = Device.objects.filter(rack=self.initial['rack']) else: - self.fields['device'].choices = [] + self.fields['device'].queryset = Device.objects.filter(site=poweroutlet.device.site, + rack__isnull=True) # Initialize port choices if self.is_bound: @@ -1158,22 +1277,55 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): # class InterfaceConnectionForm(BootstrapMixin, forms.ModelForm): - interface_a = forms.ChoiceField(choices=[], widget=SelectWithDisabled, label='Interface') - site_b = forms.ModelChoiceField(queryset=Site.objects.all(), label='Site', required=False, - widget=forms.Select(attrs={'filter-for': 'rack_b'})) - rack_b = forms.ModelChoiceField(queryset=Rack.objects.all(), label='Rack', required=False, - widget=APISelect(api_url='/api/dcim/racks/?site_id={{site_b}}', - attrs={'filter-for': 'device_b'})) - device_b = forms.ModelChoiceField(queryset=Device.objects.all(), label='Device', required=False, - widget=APISelect(api_url='/api/dcim/devices/?rack_id={{rack_b}}', - display_field='display_name', - attrs={'filter-for': 'interface_b'})) - livesearch = forms.CharField(required=False, label='Device', widget=Livesearch( - query_key='q', query_url='dcim-api:device_list', field_to_update='device_b') + interface_a = forms.ChoiceField( + choices=[], + widget=SelectWithDisabled, + label='Interface' + ) + site_b = forms.ModelChoiceField( + queryset=Site.objects.all(), + label='Site', + required=False, + widget=forms.Select( + attrs={'filter-for': 'rack_b'} + ) + ) + rack_b = forms.ModelChoiceField( + queryset=Rack.objects.all(), + label='Rack', + required=False, + widget=APISelect( + api_url='/api/dcim/racks/?site_id={{site_b}}', + attrs={'filter-for': 'device_b', 'nullable': 'true'} + ) + ) + device_b = forms.ModelChoiceField( + queryset=Device.objects.all(), + label='Device', + required=False, + widget=APISelect( + api_url='/api/dcim/devices/?site_id={{site_b}}&rack_id={{rack_b}}', + display_field='display_name', + attrs={'filter-for': 'interface_b'} + ) + ) + livesearch = forms.CharField( + required=False, + label='Device', + widget=Livesearch( + query_key='q', + query_url='dcim-api:device_list', + field_to_update='device_b' + ) + ) + interface_b = forms.ModelChoiceField( + queryset=Interface.objects.all(), + label='Interface', + widget=APISelect( + api_url='/api/dcim/devices/{{device_b}}/interfaces/?type=physical', + disabled_indicator='is_connected' + ) ) - interface_b = forms.ModelChoiceField(queryset=Interface.objects.all(), label='Interface', - widget=APISelect(api_url='/api/dcim/devices/{{device_b}}/interfaces/?type=physical', - disabled_indicator='is_connected')) class Meta: model = InterfaceConnection @@ -1198,11 +1350,15 @@ class InterfaceConnectionForm(BootstrapMixin, forms.ModelForm): else: self.fields['rack_b'].choices = [] - # Initialize device_b choices if rack_b is set + # Initialize device_b choices if rack_b or site_b is set if self.is_bound and self.data.get('rack_b'): self.fields['device_b'].queryset = Device.objects.filter(rack__pk=self.data['rack_b']) + elif self.is_bound and self.data.get('site_b'): + self.fields['device_b'].queryset = Device.objects.filter(site__pk=self.data['site_b'], rack__isnull=True) elif self.initial.get('rack_b'): self.fields['device_b'].queryset = Device.objects.filter(rack=self.initial['rack_b']) + elif self.initial.get('site_b'): + self.fields['device_b'].queryset = Device.objects.filter(site=self.initial['site_b'], rack__isnull=True) else: self.fields['device_b'].choices = [] @@ -1223,13 +1379,21 @@ class InterfaceConnectionForm(BootstrapMixin, forms.ModelForm): class InterfaceConnectionCSVForm(forms.Form): - device_a = FlexibleModelChoiceField(queryset=Device.objects.all(), to_field_name='name', - error_messages={'invalid_choice': 'Device A not found.'}) + device_a = FlexibleModelChoiceField( + queryset=Device.objects.all(), + to_field_name='name', + error_messages={'invalid_choice': 'Device A not found.'} + ) interface_a = forms.CharField() - device_b = FlexibleModelChoiceField(queryset=Device.objects.all(), to_field_name='name', - error_messages={'invalid_choice': 'Device B not found.'}) + device_b = FlexibleModelChoiceField( + queryset=Device.objects.all(), + to_field_name='name', + error_messages={'invalid_choice': 'Device B not found.'} + ) interface_b = forms.CharField() - status = forms.CharField(validators=[validate_connection_status]) + status = forms.CharField( + validators=[validate_connection_status] + ) def clean(self): diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index f2e8a13ed..df93e61f3 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1411,7 +1411,7 @@ def interfaceconnection_add(request, pk): else: form = forms.InterfaceConnectionForm(device, initial={ 'interface_a': request.GET.get('interface_a', None), - 'site_b': request.GET.get('site_b', device.rack.site), + 'site_b': request.GET.get('site_b', device.site), 'rack_b': request.GET.get('rack_b', None), 'device_b': request.GET.get('device_b', None), 'interface_b': request.GET.get('interface_b', None), diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 4b9d8ddf5..ba0d02584 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -307,10 +307,10 @@ class IPAddressForm(BootstrapMixin, CustomFieldForm): nat_inside = self.instance.nat_inside # If the IP is assigned to an interface, populate site/device fields accordingly if self.instance.nat_inside.interface: - self.initial['nat_site'] = self.instance.nat_inside.interface.device.rack.site.pk + self.initial['nat_site'] = self.instance.nat_inside.interface.device.site.pk self.initial['nat_device'] = self.instance.nat_inside.interface.device.pk self.fields['nat_device'].queryset = Device.objects.filter( - rack__site=nat_inside.interface.device.rack.site) + rack__site=nat_inside.interface.device.site) self.fields['nat_inside'].queryset = IPAddress.objects.filter( interface__device=nat_inside.interface.device) else: diff --git a/netbox/project-static/js/forms.js b/netbox/project-static/js/forms.js index 5a736627e..c86a33fcc 100644 --- a/netbox/project-static/js/forms.js +++ b/netbox/project-static/js/forms.js @@ -68,38 +68,42 @@ $(document).ready(function() { }); // API select widget - $('select[filter-for]').change(function () { + $('select[filter-for]').change(function() { // Resolve child field by ID specified in parent var child_name = $(this).attr('filter-for'); var child_field = $('#id_' + child_name); var child_selected = child_field.val(); - // Wipe out any existing options within the child field + // Wipe out any existing options within the child field and create a default option child_field.empty(); - child_field.append($("").attr("value", "").text("")); - - if ($(this).val()) { + var default_option = $("").attr("value", "").text("---------"); + if (child_field.attr('nullable') == 'true') { + default_option.attr("value", "0"); + } + child_field.append(default_option); + if ($(this).val() || $(this).attr('nullable') == 'true') { var api_url = child_field.attr('api-url'); var disabled_indicator = child_field.attr('disabled-indicator'); var initial_value = child_field.attr('initial'); var display_field = child_field.attr('display-field') || 'name'; - // Gather the values of all other filter fields for this child - $("select[filter-for='" + child_name + "']").each(function() { - var filter_field = $(this); + // Determine the filter fields needed to make an API call + var filter_regex = /\{\{([a-z_]+)\}\}/g; + var match; + while (match = filter_regex.exec(api_url)) { + var filter_field = $('#id_' + match[1]); if (filter_field.val()) { - api_url = api_url.replace('{{' + filter_field.attr('name') + '}}', filter_field.val()); + api_url = api_url.replace(match[0], filter_field.val()); } else { - // Not all filters have been selected yet - return false; + api_url = api_url.replace(match[0], '0'); } - - }); + } // If all URL variables have been replaced, make the API call if (api_url.search('{{') < 0) { + console.log(child_name + ": Fetching " + api_url); $.ajax({ url: api_url, dataType: 'json', diff --git a/netbox/templates/dcim/consoleport_connect.html b/netbox/templates/dcim/consoleport_connect.html index c237bc2c9..f896ebbd5 100644 --- a/netbox/templates/dcim/consoleport_connect.html +++ b/netbox/templates/dcim/consoleport_connect.html @@ -7,6 +7,9 @@ {% block content %}
{% csrf_token %} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %}
{% if form.non_field_errors %} @@ -29,6 +32,12 @@ {% render_field form.livesearch %}
+
+ +
+

{{ consoleport.device.site }}

+
+
{% render_field form.rack %} {% render_field form.console_server %}
diff --git a/netbox/templates/dcim/consoleserverport_connect.html b/netbox/templates/dcim/consoleserverport_connect.html index e747a9d57..08850d1ed 100644 --- a/netbox/templates/dcim/consoleserverport_connect.html +++ b/netbox/templates/dcim/consoleserverport_connect.html @@ -6,7 +6,10 @@ {% block content %} -{% csrf_token %} + {% csrf_token %} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %}
{% if form.non_field_errors %} @@ -29,6 +32,12 @@ {% render_field form.livesearch %}
+
+ +
+

{{ consoleserverport.device.site }}

+
+
{% render_field form.rack %} {% render_field form.device %}
diff --git a/netbox/templates/dcim/inc/device_header.html b/netbox/templates/dcim/inc/device_header.html index 4737d59c6..9f31c73fc 100644 --- a/netbox/templates/dcim/inc/device_header.html +++ b/netbox/templates/dcim/inc/device_header.html @@ -3,7 +3,7 @@