mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-08 00:28:16 -06:00
Merge e3cbdce108
into 651d462456
This commit is contained in:
commit
7a6719595a
@ -48,6 +48,19 @@ IFACE_ORDERING_CHOICES = [
|
|||||||
[IFACE_ORDERING_NAME, 'Name (alphabetically)']
|
[IFACE_ORDERING_NAME, 'Name (alphabetically)']
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Interface enabled as choice
|
||||||
|
IFACE_DISABLED = False
|
||||||
|
IFACE_ENABLED = True
|
||||||
|
IFACE_ENABLED_CHOICES = [
|
||||||
|
[IFACE_DISABLED, 'Disabled'],
|
||||||
|
[IFACE_ENABLED, 'Enabled'],
|
||||||
|
]
|
||||||
|
|
||||||
|
IFACE_STATUS_CLASSES = {
|
||||||
|
0: 'disabled',
|
||||||
|
1: 'enabled',
|
||||||
|
}
|
||||||
|
|
||||||
# Interface form factors
|
# Interface form factors
|
||||||
# Virtual
|
# Virtual
|
||||||
IFACE_FF_VIRTUAL = 0
|
IFACE_FF_VIRTUAL = 0
|
||||||
|
@ -581,6 +581,94 @@ class InterfaceFilter(django_filters.FilterSet):
|
|||||||
return queryset.none()
|
return queryset.none()
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceListFilter(django_filters.FilterSet):
|
||||||
|
q = django_filters.CharFilter(
|
||||||
|
method='search',
|
||||||
|
label='Search',
|
||||||
|
)
|
||||||
|
device = django_filters.CharFilter(
|
||||||
|
method='filter_device',
|
||||||
|
name='name',
|
||||||
|
label='Device',
|
||||||
|
)
|
||||||
|
site = django_filters.CharFilter(
|
||||||
|
method='filter_site',
|
||||||
|
label='Site (slug)',
|
||||||
|
)
|
||||||
|
role = django_filters.CharFilter(
|
||||||
|
method='filter_role',
|
||||||
|
label='Role (slug)',
|
||||||
|
)
|
||||||
|
rack_group_id = NullableModelMultipleChoiceFilter(
|
||||||
|
name='device__rack__group',
|
||||||
|
queryset=RackGroup.objects.all(),
|
||||||
|
label='Rack Group(ID)',
|
||||||
|
)
|
||||||
|
rack_id = NullableModelMultipleChoiceFilter(
|
||||||
|
name='device__rack',
|
||||||
|
queryset=Rack.objects.all(),
|
||||||
|
label='Rack (ID)',
|
||||||
|
)
|
||||||
|
type = django_filters.CharFilter(
|
||||||
|
method='filter_type',
|
||||||
|
label='Interface type',
|
||||||
|
)
|
||||||
|
mac_address = django_filters.CharFilter(
|
||||||
|
method='_mac_address',
|
||||||
|
label='MAC address',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Interface
|
||||||
|
fields = ['form_factor', 'enabled', 'mtu']
|
||||||
|
|
||||||
|
def filter_device(self, queryset, name, value):
|
||||||
|
try:
|
||||||
|
device = Device.objects.select_related('device_type').get(**{name: value})
|
||||||
|
ordering = device.device_type.interface_ordering
|
||||||
|
return queryset.filter(device=device).order_naturally(ordering)
|
||||||
|
except Device.DoesNotExist:
|
||||||
|
return queryset.none()
|
||||||
|
|
||||||
|
def filter_site(self, queryset, name, value):
|
||||||
|
if not value.strip():
|
||||||
|
return queryset
|
||||||
|
return queryset.filter(device__site__slug=value)
|
||||||
|
|
||||||
|
def filter_role(self, queryset, name, value):
|
||||||
|
if not value.strip():
|
||||||
|
return queryset
|
||||||
|
return queryset.filter(device__device_role__slug=value)
|
||||||
|
|
||||||
|
def filter_type(self, queryset, name, value):
|
||||||
|
value = value.strip().lower()
|
||||||
|
return {
|
||||||
|
'physical': queryset.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES),
|
||||||
|
'virtual': queryset.filter(form_factor__in=VIRTUAL_IFACE_TYPES),
|
||||||
|
'wireless': queryset.filter(form_factor__in=WIRELESS_IFACE_TYPES),
|
||||||
|
'lag': queryset.filter(form_factor=IFACE_FF_LAG),
|
||||||
|
}.get(value, queryset.none())
|
||||||
|
|
||||||
|
def _mac_address(self, queryset, name, value):
|
||||||
|
value = value.strip()
|
||||||
|
if not value:
|
||||||
|
return queryset
|
||||||
|
try:
|
||||||
|
return queryset.filter(mac_address__icontains=value)
|
||||||
|
except AddrFormatError:
|
||||||
|
return queryset.none()
|
||||||
|
|
||||||
|
def search(self, queryset, name, value):
|
||||||
|
if not value.strip():
|
||||||
|
return queryset
|
||||||
|
return queryset.filter(
|
||||||
|
Q(device__name__icontains=value.strip()) |
|
||||||
|
Q(name__icontains=value.strip()) |
|
||||||
|
Q(description__icontains=value.strip()) |
|
||||||
|
Q(mac_address__icontains=value.strip())
|
||||||
|
).distinct()
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayFilter(DeviceComponentFilterSet):
|
class DeviceBayFilter(DeviceComponentFilterSet):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -21,7 +21,7 @@ from .formfields import MACAddressFormField
|
|||||||
from .models import (
|
from .models import (
|
||||||
DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_CONNECTED, ConsolePort,
|
DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_CONNECTED, ConsolePort,
|
||||||
ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType, Interface,
|
ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType, Interface,
|
||||||
IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_ORDERING_CHOICES, InterfaceConnection, InterfaceTemplate, Manufacturer,
|
IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_ENABLED_CHOICES, IFACE_ORDERING_CHOICES, InterfaceConnection, InterfaceTemplate, Manufacturer,
|
||||||
InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, RACK_FACE_CHOICES,
|
InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, RACK_FACE_CHOICES,
|
||||||
RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES, Rack, RackGroup, RackReservation, RackRole, RACK_WIDTH_19IN, RACK_WIDTH_23IN,
|
RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES, Rack, RackGroup, RackReservation, RackRole, RACK_WIDTH_19IN, RACK_WIDTH_23IN,
|
||||||
Region, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD, SUBDEVICE_ROLE_PARENT,
|
Region, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD, SUBDEVICE_ROLE_PARENT,
|
||||||
@ -1540,6 +1540,114 @@ class InterfaceBulkDisconnectForm(ConfirmationForm):
|
|||||||
pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput)
|
pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput)
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceCSVForm(forms.ModelForm):
|
||||||
|
device = FlexibleModelChoiceField(
|
||||||
|
queryset=Device.objects.all(),
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Name or ID of device',
|
||||||
|
error_messages={'invalid_choice': 'Device not found.'}
|
||||||
|
)
|
||||||
|
name = forms.CharField(
|
||||||
|
help_text='Name of interface'
|
||||||
|
)
|
||||||
|
lag = FlexibleModelChoiceField(
|
||||||
|
queryset=Interface.objects.filter(form_factor=IFACE_FF_LAG),
|
||||||
|
to_field_name='name',
|
||||||
|
required=False,
|
||||||
|
help_text='Lag Name or ID of interface',
|
||||||
|
error_messages={'invalid_choice': 'Lag not found.'}
|
||||||
|
)
|
||||||
|
mac_address = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
help_text='MAC address of interface'
|
||||||
|
)
|
||||||
|
form_factor = forms.IntegerField(
|
||||||
|
required=False,
|
||||||
|
help_text='Interface Form Factor'
|
||||||
|
)
|
||||||
|
description = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
help_text='Description for interface'
|
||||||
|
)
|
||||||
|
enabled = forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
help_text='Enabled/Disabled'
|
||||||
|
)
|
||||||
|
mtu = forms.IntegerField(
|
||||||
|
required=False,
|
||||||
|
help_text='MTU'
|
||||||
|
)
|
||||||
|
mgmt_only = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
help_text='Management Only'
|
||||||
|
)
|
||||||
|
is_virtual = forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
help_text='Is Virtual?'
|
||||||
|
)
|
||||||
|
is_wireless = forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
help_text='Is Wireless?'
|
||||||
|
)
|
||||||
|
is_lag = forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
help_text='Is Lag?'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Interface
|
||||||
|
fields = ('device', 'lag', 'name', 'mac_address', 'form_factor', 'enabled', 'description', 'mtu', 'mgmt_only', 'is_virtual', 'is_wireless', 'is_lag')
|
||||||
|
nullable_fields = ['lag', 'is_virtual', 'is_wireless', 'is_lag']
|
||||||
|
|
||||||
|
def clean_interface(self):
|
||||||
|
interface_name = self.cleaned_data.get('interface_name')
|
||||||
|
if not interface:
|
||||||
|
return None
|
||||||
|
return interface
|
||||||
|
|
||||||
|
def clean_lag(self):
|
||||||
|
device_id = self.cleaned_data.get('device')
|
||||||
|
lag_name = self.cleaned_data.get('lag')
|
||||||
|
if device_id is not None and lag_name is not None:
|
||||||
|
lag = Interface.objects.filter(
|
||||||
|
device=device_id, form_factor=IFACE_FF_LAG).get(
|
||||||
|
name=lag_name
|
||||||
|
)
|
||||||
|
if not lag_name:
|
||||||
|
return None
|
||||||
|
return lag
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceFilterForm(BootstrapMixin, forms.Form):
|
||||||
|
site = forms.ModelChoiceField(required=False, queryset=Site.objects.all(), to_field_name='slug')
|
||||||
|
device = forms.CharField(required=False, label='Device name')
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceListFilterForm(BootstrapMixin, forms.Form):
|
||||||
|
q = forms.CharField(required=False, label='Search')
|
||||||
|
site = forms.ModelChoiceField(required=False, queryset=Site.objects.all(), to_field_name='slug')
|
||||||
|
rack_group_id = FilterChoiceField(
|
||||||
|
required=False,
|
||||||
|
queryset=RackGroup.objects.select_related('site').annotate(filter_count=Count('racks__devices')),
|
||||||
|
label='Rack Group',
|
||||||
|
null_option=(0, 'None')
|
||||||
|
)
|
||||||
|
rack_id = FilterChoiceField(
|
||||||
|
required=False,
|
||||||
|
queryset=Rack.objects.annotate(filter_count=Count('devices')),
|
||||||
|
label='Rack',
|
||||||
|
null_option=(0, 'None')
|
||||||
|
)
|
||||||
|
enabled = forms.ChoiceField(choices=add_blank_choice(IFACE_ENABLED_CHOICES), required=False)
|
||||||
|
role = FilterChoiceField(
|
||||||
|
required=False,
|
||||||
|
queryset=DeviceRole.objects.annotate(filter_count=Count('devices')),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Device Role'
|
||||||
|
)
|
||||||
|
device = forms.CharField(required=False, label='Device Name')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Interface connections
|
# Interface connections
|
||||||
#
|
#
|
||||||
|
@ -1203,6 +1203,8 @@ class Interface(models.Model):
|
|||||||
|
|
||||||
objects = InterfaceQuerySet.as_manager()
|
objects = InterfaceQuerySet.as_manager()
|
||||||
|
|
||||||
|
csv_headers = ['device', 'lag', 'name', 'mac_address', 'form_factor', 'enabled', 'description', 'mtu', 'mgmt_only', 'is_virtual', 'is_wireless', 'is_connected', 'is_lag']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device', 'name']
|
ordering = ['device', 'name']
|
||||||
unique_together = ['device', 'name']
|
unique_together = ['device', 'name']
|
||||||
@ -1223,7 +1225,7 @@ class Interface(models.Model):
|
|||||||
if self.lag and self.lag.device != self.device:
|
if self.lag and self.lag.device != self.device:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'lag': "The selected LAG interface ({}) belongs to a different device ({}).".format(
|
'lag': "The selected LAG interface ({}) belongs to a different device ({}).".format(
|
||||||
self.lag.name, self.lag.device.name
|
self.lag.name, self.lag.device.name
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1287,6 +1289,27 @@ class Interface(models.Model):
|
|||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_status_class(self):
|
||||||
|
return IFACE_STATUS_CLASSES[self.enabled]
|
||||||
|
|
||||||
|
# Used for export
|
||||||
|
def to_csv(self):
|
||||||
|
return csv_format([
|
||||||
|
self.device.identifier,
|
||||||
|
self.lag,
|
||||||
|
self.name,
|
||||||
|
self.mac_address,
|
||||||
|
self.form_factor,
|
||||||
|
self.enabled,
|
||||||
|
self.description,
|
||||||
|
self.mtu,
|
||||||
|
self.mgmt_only,
|
||||||
|
self.is_virtual,
|
||||||
|
self.is_wireless,
|
||||||
|
self.is_connected,
|
||||||
|
self.is_lag,
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class InterfaceConnection(models.Model):
|
class InterfaceConnection(models.Model):
|
||||||
"""
|
"""
|
||||||
|
@ -39,6 +39,12 @@ DEVICE_LINK = """
|
|||||||
</a>
|
</a>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
INTERFACE_LINK = """
|
||||||
|
<a href="{% url 'dcim:device' pk=record.device.pk %}">
|
||||||
|
{{ record.name|default:'<span class="label label-info"> - </span>' }}
|
||||||
|
</a>
|
||||||
|
"""
|
||||||
|
|
||||||
REGION_ACTIONS = """
|
REGION_ACTIONS = """
|
||||||
{% if perms.dcim.change_region %}
|
{% if perms.dcim.change_region %}
|
||||||
<a href="{% url 'dcim:region_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
|
<a href="{% url 'dcim:region_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
|
||||||
@ -97,6 +103,10 @@ DEVICE_STATUS = """
|
|||||||
<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
|
<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
INTERFACE_ENABLED = """
|
||||||
|
<span class="label label-{{ record.get_status_class }}">{{ record.enabled }}</span>
|
||||||
|
"""
|
||||||
|
|
||||||
DEVICE_PRIMARY_IP = """
|
DEVICE_PRIMARY_IP = """
|
||||||
{{ record.primary_ip6.address.ip|default:"" }}
|
{{ record.primary_ip6.address.ip|default:"" }}
|
||||||
{% if record.primary_ip6 and record.primary_ip4 %}<br />{% endif %}
|
{% if record.primary_ip6 and record.primary_ip4 %}<br />{% endif %}
|
||||||
@ -523,3 +533,38 @@ class InterfaceConnectionTable(BaseTable):
|
|||||||
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')
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceImportTable(BaseTable):
|
||||||
|
device = tables.LinkColumn('dcim:device', accessor=Accessor('interface.device'),
|
||||||
|
args=[Accessor('interface.device.pk')], verbose_name='Device')
|
||||||
|
lag = tables.LinkColumn('dcim:interface', accessor=Accessor('self.name'),
|
||||||
|
args=[Accessor('self.pk')], verbose_name='Lag ID')
|
||||||
|
name = tables.Column(verbose_name='Interface')
|
||||||
|
mac_address = tables.Column(verbose_name='MAC Address')
|
||||||
|
form_factor = tables.Column(verbose_name='Form Factor')
|
||||||
|
enabled = tables.Column(verbose_name='Enabled')
|
||||||
|
description = tables.Column(verbose_name='Description')
|
||||||
|
mtu = tables.Column(verbose_name='MTU')
|
||||||
|
mgmt_only = tables.Column(verbose_name='MGMT Only')
|
||||||
|
is_virtual = tables.Column(verbose_name='Is Virtual?')
|
||||||
|
is_wireless = tables.Column(verbose_name='Is Wireless?')
|
||||||
|
is_lag = tables.Column(verbose_name='Is Lag?')
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = Interface
|
||||||
|
fields = ('device', 'lag', 'name', 'mac_address', 'form_factor', 'enabled', 'description', 'mtu', 'mgmt_only', 'is_virtual', 'is_wireless', 'is_lag')
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceListTable(BaseTable):
|
||||||
|
device = tables.LinkColumn('dcim:device', accessor=Accessor('device'),
|
||||||
|
args=[Accessor('device.pk')], verbose_name='Device')
|
||||||
|
name = tables.TemplateColumn(template_code=INTERFACE_LINK, verbose_name='Interface')
|
||||||
|
enabled = tables.TemplateColumn(template_code=INTERFACE_ENABLED, verbose_name='Enabled')
|
||||||
|
form_factor = tables.Column(verbose_name='Form Factor')
|
||||||
|
mac_address = tables.Column(verbose_name='MAC Address')
|
||||||
|
description = tables.Column(verbose_name='Description')
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = Interface
|
||||||
|
fields = ('device', 'name', 'enabled', 'form_factor', 'mac_address', 'description')
|
||||||
|
@ -177,6 +177,8 @@ urlpatterns = [
|
|||||||
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'^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'),
|
||||||
|
url(r'^interfaces/$', views.InterfaceListView.as_view(), name='interface_list'),
|
||||||
|
url(r'^interfaces/import/$', views.InterfaceBulkImportView.as_view(), name='interface_import'),
|
||||||
|
|
||||||
# Device bays
|
# Device bays
|
||||||
url(r'^devices/device-bays/add/$', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
|
url(r'^devices/device-bays/add/$', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
|
||||||
|
@ -1543,6 +1543,21 @@ class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|||||||
table = tables.InterfaceTable
|
table = tables.InterfaceTable
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||||
|
permission_required = 'dcim.change_interface'
|
||||||
|
model_form = forms.InterfaceCSVForm
|
||||||
|
table = tables.InterfaceImportTable
|
||||||
|
default_return_url = 'dcim:interface_list'
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceListView(ObjectListView):
|
||||||
|
queryset = Interface.objects.all()
|
||||||
|
filter = filters.InterfaceListFilter
|
||||||
|
filter_form = forms.InterfaceListFilterForm
|
||||||
|
table = tables.InterfaceListTable
|
||||||
|
template_name = 'dcim/interface_list.html'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device bays
|
# Device bays
|
||||||
#
|
#
|
||||||
|
@ -390,4 +390,18 @@ td .progress {
|
|||||||
}
|
}
|
||||||
textarea {
|
textarea {
|
||||||
font-family: Consolas, Lucida Console, monospace;
|
font-family: Consolas, Lucida Console, monospace;
|
||||||
|
}
|
||||||
|
.label-enabled {
|
||||||
|
background-color: #6fd86f;
|
||||||
|
}
|
||||||
|
.label-enabled[href]:hover,
|
||||||
|
.label-enabled[href]:focus {
|
||||||
|
background-color: #3ccd3c;
|
||||||
|
}
|
||||||
|
.label-disabled {
|
||||||
|
background-color: #d86f6f;
|
||||||
|
}
|
||||||
|
.label-disabled[href]:hover,
|
||||||
|
.label-disabled[href]:focus {
|
||||||
|
background-color: #cd3c3c;
|
||||||
}
|
}
|
@ -85,6 +85,10 @@
|
|||||||
<li class="subnav"><a href="{% url 'dcim:device_add' %}"><i class="fa fa-plus"></i> Add a Device</a></li>
|
<li class="subnav"><a href="{% url 'dcim:device_add' %}"><i class="fa fa-plus"></i> Add a Device</a></li>
|
||||||
<li class="subnav"><a href="{% url 'dcim:device_import' %}"><i class="fa fa-download"></i> Import Devices</a></li>
|
<li class="subnav"><a href="{% url 'dcim:device_import' %}"><i class="fa fa-download"></i> Import Devices</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<li><a href="{% url 'dcim:interface_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Interfaces</a></li>
|
||||||
|
{% if perms.dcim.add_interface %}
|
||||||
|
<li><a href="{% url 'dcim:interface_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Interfaces</a></li>
|
||||||
|
{% endif %}
|
||||||
{% if perms.ipam.add_device or perms.ipam.add_devicetype %}
|
{% if perms.ipam.add_device or perms.ipam.add_devicetype %}
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -374,6 +374,17 @@
|
|||||||
<input type="hidden" name="device" value="{{ device.pk }}" />
|
<input type="hidden" name="device" value="{{ device.pk }}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
<div class="pull-right">
|
||||||
|
{% if perms.dcim.add_interface %}
|
||||||
|
<a href="{% url 'dcim:interface_import' %}" class="btn btn-info">
|
||||||
|
<span class="fa fa-download" aria-hidden="true"></span>
|
||||||
|
Import Interfaces
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'dcim:interface_list' %}?device={{ device.name }}&export" class="btn btn-success">
|
||||||
|
<span class="fa fa-upload" aria-hidden="true"></span>
|
||||||
|
Export Interfaces
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<strong>Interfaces</strong>
|
<strong>Interfaces</strong>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
@ -619,6 +630,20 @@ $('button.toggle-ips').click(function() {
|
|||||||
$(this).children('span').toggleClass('glyphicon-check glyphicon-unchecked');
|
$(this).children('span').toggleClass('glyphicon-check glyphicon-unchecked');
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
// Toggle the display of Descriptions under interfaces
|
||||||
|
$('button.toggle-description').click(function() {
|
||||||
|
var selected = $(this).attr('selected');
|
||||||
|
if (selected) {
|
||||||
|
$('span.iface-description').hide();
|
||||||
|
$('i.fa-comment-o').show();
|
||||||
|
} else {
|
||||||
|
$('span.iface-description').show();
|
||||||
|
$('i.fa-comment-o').hide();
|
||||||
|
}
|
||||||
|
$(this).attr('selected', !selected);
|
||||||
|
$(this).children('span').toggleClass('glyphicon-check glyphicon-unchecked');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<script src="{% static 'js/graphs.js' %}?v{{ settings.VERSION }}"></script>
|
<script src="{% static 'js/graphs.js' %}?v{{ settings.VERSION }}"></script>
|
||||||
<script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
|
<script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
|
||||||
|
24
netbox/templates/dcim/interface_list.html
Normal file
24
netbox/templates/dcim/interface_list.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{% extends '_base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Interfaces{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="pull-right">
|
||||||
|
{% if perms.dcim.add_interface %}
|
||||||
|
<a href="{% url 'dcim:interface_import' %}" class="btn btn-info">
|
||||||
|
<span class="fa fa-download" aria-hidden="true"></span>
|
||||||
|
Import Interfaces
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% include 'inc/export_button.html' with obj_type='interfaces' %}
|
||||||
|
</div>
|
||||||
|
<h1>Interfaces</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-9">
|
||||||
|
{% include 'responsive_table.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
{% include 'inc/search_panel.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user