mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-08 00:28:16 -06:00
Added additional field connection_name for interface connection
Added additional field connection_name for interface connection to track e.g. cable numbers. Therefore it was necessary to change the edit the template as well.
This commit is contained in:
parent
ec0cb7a8bc
commit
e44d0c3ea5
@ -1692,10 +1692,67 @@ class InterfaceBulkDisconnectForm(ConfirmationForm):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class InterfaceConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
class InterfaceConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
||||||
interface_a = forms.ChoiceField(
|
site_a = forms.ModelChoiceField(
|
||||||
choices=[],
|
queryset=Site.objects.all(),
|
||||||
|
label='Site',
|
||||||
|
required=False,
|
||||||
|
widget=forms.Select(
|
||||||
|
attrs={'filter-for': 'rack_a'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
rack_a = ChainedModelChoiceField(
|
||||||
|
queryset=Rack.objects.all(),
|
||||||
|
chains=(
|
||||||
|
('site', 'site_a'),
|
||||||
|
),
|
||||||
|
label='Rack',
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/racks/?site_id={{site_a}}',
|
||||||
|
attrs={'filter-for': 'device_a', 'nullable': 'true'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
device_a = ChainedModelChoiceField(
|
||||||
|
queryset=Device.objects.all(),
|
||||||
|
chains=(
|
||||||
|
('site', 'site_a'),
|
||||||
|
('rack', 'rack_a'),
|
||||||
|
),
|
||||||
|
label='Device',
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/devices/?site_id={{site_a}}&rack_id={{rack_a}}',
|
||||||
|
display_field='display_name',
|
||||||
|
attrs={'filter-for': 'interface_a'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
livesearch_a = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label='Device',
|
||||||
|
widget=Livesearch(
|
||||||
|
query_key='q',
|
||||||
|
query_url='dcim-api:device-list',
|
||||||
|
field_to_update='device_a'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
#interface_a = forms.ChoiceField(
|
||||||
|
# choices=[],
|
||||||
|
# widget=SelectWithDisabled,
|
||||||
|
# label='Interface'
|
||||||
|
#)
|
||||||
|
interface_a = ChainedModelChoiceField(
|
||||||
|
queryset=Interface.objects.connectable().select_related(
|
||||||
|
'circuit_termination', 'connected_as_a', 'connected_as_b'
|
||||||
|
),
|
||||||
|
chains=(
|
||||||
|
('device', 'device_a'),
|
||||||
|
),
|
||||||
|
label='Interface',
|
||||||
widget=SelectWithDisabled,
|
widget=SelectWithDisabled,
|
||||||
label='Interface'
|
#widget=APISelect(
|
||||||
|
# api_url='/api/dcim/interfaces/?device_id={{device_a}}&type=physical',
|
||||||
|
# disabled_indicator='is_connected'
|
||||||
|
#)
|
||||||
)
|
)
|
||||||
site_b = forms.ModelChoiceField(
|
site_b = forms.ModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
@ -1756,19 +1813,33 @@ class InterfaceConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelFor
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InterfaceConnection
|
model = InterfaceConnection
|
||||||
fields = ['interface_a', 'site_b', 'rack_b', 'device_b', 'interface_b', 'livesearch', 'connection_status']
|
fields = ['site_a','rack_a','device_a','interface_a', 'livesearch_a','site_b', 'rack_b', 'device_b', 'interface_b', 'livesearch', 'connection_status','connection_name']
|
||||||
|
|
||||||
def __init__(self, device_a, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
super(InterfaceConnectionForm, self).__init__(*args, **kwargs)
|
super(InterfaceConnectionForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Initialize interface A choices
|
#if self.instance.pk:
|
||||||
device_a_interfaces = Interface.objects.connectable().order_naturally().filter(device=device_a).select_related(
|
|
||||||
'circuit_termination', 'connected_as_a', 'connected_as_b'
|
|
||||||
)
|
#else:
|
||||||
self.fields['interface_a'].choices = [
|
|
||||||
(iface.id, {'label': iface.name, 'disabled': iface.is_connected}) for iface in device_a_interfaces
|
if self.initial.get('device_a'):
|
||||||
]
|
|
||||||
|
# Initialize interface A choices
|
||||||
|
device_a_interfaces = Interface.objects.connectable().order_naturally().filter(device=self.initial['device_a']).select_related(
|
||||||
|
'circuit_termination', 'connected_as_a', 'connected_as_b'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fields['interface_a'].choices = [
|
||||||
|
(iface.id, {'label': iface.name, 'disabled': iface.is_connected}) for iface in device_a_interfaces
|
||||||
|
]
|
||||||
|
|
||||||
|
# Mark connected interfaces as disabled
|
||||||
|
if self.data.get('device_a'):
|
||||||
|
self.fields['interface_a'].choices = [
|
||||||
|
(iface.id, {'label': iface.name, 'disabled': iface.is_connected}) for iface in self.fields['interface_a'].queryset
|
||||||
|
]
|
||||||
|
|
||||||
# Mark connected interfaces as disabled
|
# Mark connected interfaces as disabled
|
||||||
if self.data.get('device_b'):
|
if self.data.get('device_b'):
|
||||||
@ -1800,10 +1871,14 @@ class InterfaceConnectionCSVForm(forms.ModelForm):
|
|||||||
choices=CONNECTION_STATUS_CHOICES,
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
help_text='Connection status'
|
help_text='Connection status'
|
||||||
)
|
)
|
||||||
|
connection_name = forms.CharField(
|
||||||
|
help_text='Name of this connection (e.g. cable-number)',
|
||||||
|
required = False
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InterfaceConnection
|
model = InterfaceConnection
|
||||||
fields = ['device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status']
|
fields = ['device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status','connection_name']
|
||||||
|
|
||||||
def clean_interface_a(self):
|
def clean_interface_a(self):
|
||||||
|
|
||||||
|
@ -1370,8 +1370,9 @@ class InterfaceConnection(models.Model):
|
|||||||
interface_b = models.OneToOneField('Interface', related_name='connected_as_b', on_delete=models.CASCADE)
|
interface_b = models.OneToOneField('Interface', related_name='connected_as_b', on_delete=models.CASCADE)
|
||||||
connection_status = models.BooleanField(choices=CONNECTION_STATUS_CHOICES, default=CONNECTION_STATUS_CONNECTED,
|
connection_status = models.BooleanField(choices=CONNECTION_STATUS_CHOICES, default=CONNECTION_STATUS_CONNECTED,
|
||||||
verbose_name='Status')
|
verbose_name='Status')
|
||||||
|
connection_name = models.CharField(max_length = 64,blank = True)
|
||||||
|
|
||||||
csv_headers = ['device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status']
|
csv_headers = ['device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status','connection_name']
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
try:
|
try:
|
||||||
@ -1390,6 +1391,7 @@ class InterfaceConnection(models.Model):
|
|||||||
self.interface_b.device.identifier,
|
self.interface_b.device.identifier,
|
||||||
self.interface_b.name,
|
self.interface_b.name,
|
||||||
self.get_connection_status_display(),
|
self.get_connection_status_display(),
|
||||||
|
self.connection_name,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@ -519,7 +519,8 @@ class InterfaceConnectionTable(BaseTable):
|
|||||||
device_b = tables.LinkColumn('dcim:device', accessor=Accessor('interface_b.device'),
|
device_b = tables.LinkColumn('dcim:device', accessor=Accessor('interface_b.device'),
|
||||||
args=[Accessor('interface_b.device.pk')], verbose_name='Device B')
|
args=[Accessor('interface_b.device.pk')], verbose_name='Device B')
|
||||||
interface_b = tables.Column(verbose_name='Interface B')
|
interface_b = tables.Column(verbose_name='Interface B')
|
||||||
|
connection_name = tables.Column(verbose_name='Connection Name')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ('device_a', 'interface_a', 'device_b', 'interface_b')
|
fields = ('device_a', 'interface_a', 'device_b', 'interface_b','connection_name')
|
||||||
|
@ -182,6 +182,7 @@ urlpatterns = [
|
|||||||
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||||
url(r'^devices/(?P<pk>\d+)/interface-connections/add/$', views.interfaceconnection_add, name='interfaceconnection_add'),
|
url(r'^devices/(?P<pk>\d+)/interface-connections/add/$', views.interfaceconnection_add, name='interfaceconnection_add'),
|
||||||
url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.interfaceconnection_delete, name='interfaceconnection_delete'),
|
url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.interfaceconnection_delete, name='interfaceconnection_delete'),
|
||||||
|
url(r'^interface-connections/(?P<pk>\d+)/edit/$', views.InterfaceConnectionEditView.as_view(), name='interfaceconnection_edit'),
|
||||||
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'),
|
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'),
|
||||||
url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
||||||
|
|
||||||
|
@ -1682,7 +1682,7 @@ def interfaceconnection_add(request, pk):
|
|||||||
device = get_object_or_404(Device, pk=pk)
|
device = get_object_or_404(Device, pk=pk)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = forms.InterfaceConnectionForm(device, request.POST)
|
form = forms.InterfaceConnectionForm( request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
|
||||||
interfaceconnection = form.save()
|
interfaceconnection = form.save()
|
||||||
@ -1709,12 +1709,14 @@ def interfaceconnection_add(request, pk):
|
|||||||
return redirect('dcim:device', pk=device.pk)
|
return redirect('dcim:device', pk=device.pk)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = forms.InterfaceConnectionForm(device, initial={
|
form = forms.InterfaceConnectionForm(initial={
|
||||||
|
'device_a': pk,
|
||||||
'interface_a': request.GET.get('interface_a'),
|
'interface_a': request.GET.get('interface_a'),
|
||||||
'site_b': request.GET.get('site_b'),
|
'site_b': request.GET.get('site_b'),
|
||||||
'rack_b': request.GET.get('rack_b'),
|
'rack_b': request.GET.get('rack_b'),
|
||||||
'device_b': request.GET.get('device_b'),
|
'device_b': request.GET.get('device_b'),
|
||||||
'interface_b': request.GET.get('interface_b'),
|
'interface_b': request.GET.get('interface_b'),
|
||||||
|
'connection_name': request.GET.get('connection_name'),
|
||||||
})
|
})
|
||||||
|
|
||||||
return render(request, 'dcim/interfaceconnection_edit.html', {
|
return render(request, 'dcim/interfaceconnection_edit.html', {
|
||||||
@ -1768,6 +1770,21 @@ def interfaceconnection_delete(request, pk):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceConnectionEditView(PermissionRequiredMixin, ObjectEditView):
|
||||||
|
permission_required = 'dcim.edit_interfaceconnection'
|
||||||
|
model = InterfaceConnection
|
||||||
|
#parent_field = 'id'
|
||||||
|
model_form = forms.InterfaceConnectionForm
|
||||||
|
template_name = 'dcim/interfaceconnection_edit.html'
|
||||||
|
|
||||||
|
#template_name = 'dcim/device_edit.html'
|
||||||
|
#default_return_url = 'dcim:device_list'
|
||||||
|
|
||||||
|
#def alter_obj(self, obj, request, url_args, url_kwargs):
|
||||||
|
# if 'device' in url_kwargs:
|
||||||
|
# obj.device = get_object_or_404(InterfaceConnection, pk=url_kwargs['device'])
|
||||||
|
# return obj
|
||||||
|
|
||||||
class InterfaceConnectionsBulkImportView(PermissionRequiredMixin, BulkImportView):
|
class InterfaceConnectionsBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||||
permission_required = 'dcim.change_interface'
|
permission_required = 'dcim.change_interface'
|
||||||
model_form = forms.InterfaceConnectionCSVForm
|
model_form = forms.InterfaceConnectionCSVForm
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span title="{{ connected_iface.get_form_factor_display }}">{{ connected_iface }}</span>
|
<span title="{{ connected_iface.get_form_factor_display }}">{{ connected_iface }}</span>
|
||||||
|
{% if iface.connection.connection_name %}<br /> <small class="text-muted">(via {{ iface.connection.connection_name }})</small>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% elif iface.circuit_termination %}
|
{% elif iface.circuit_termination %}
|
||||||
@ -98,6 +99,9 @@
|
|||||||
<i class="fa fa-plug" aria-hidden="true"></i>
|
<i class="fa fa-plug" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<!-- <a href="{% url 'dcim:interfaceconnection_edit' pk=iface.connection.pk %}?interface_a={{ iface.pk }}" class="btn btn-info btn-xs" title="Edit Interfaceconnection">
|
||||||
|
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
||||||
|
</a> -->
|
||||||
<a href="{% url 'dcim:interfaceconnection_delete' pk=iface.connection.pk %}?device={{ device.pk }}" class="btn btn-danger btn-xs" title="Disconnect">
|
<a href="{% url 'dcim:interfaceconnection_delete' pk=iface.connection.pk %}?device={{ device.pk }}" class="btn btn-danger btn-xs" title="Disconnect">
|
||||||
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
|
@ -82,6 +82,12 @@
|
|||||||
{% render_field form.connection_status %}
|
{% render_field form.connection_status %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<!-- connection name -->
|
||||||
|
<div class="col-md-4 col-md-offset-4">
|
||||||
|
{% render_field form.connection_name %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="submit" name="_create" class="btn btn-primary">Connect</button>
|
<button type="submit" name="_create" class="btn btn-primary">Connect</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user