mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
WIP: Initial work on the cable connection form
This commit is contained in:
parent
a36b120c8b
commit
471bddea09
@ -284,12 +284,9 @@ CONNECTION_STATUS_CHOICES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Cable endpoint types
|
# Cable endpoint types
|
||||||
CABLE_ENDPOINT_TYPES = (
|
CABLE_ENDPOINT_TYPES = [
|
||||||
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport',
|
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontpanelport', 'rearpanelport',
|
||||||
)
|
]
|
||||||
CABLE_CONNECTION_TYPES = CABLE_ENDPOINT_TYPES + (
|
|
||||||
'frontpanelport', 'rearpanelport',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Cable types
|
# Cable types
|
||||||
# TODO: Add more types
|
# TODO: Add more types
|
||||||
@ -299,3 +296,24 @@ CABLE_TYPE_CHOICES = (
|
|||||||
(CABLE_TYPE_COPPER, 'Copper'),
|
(CABLE_TYPE_COPPER, 'Copper'),
|
||||||
(CABLE_TYPE_FIBER, 'Fiber'),
|
(CABLE_TYPE_FIBER, 'Fiber'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CABLE_ENDPOINT_TYPE_CHOICES = {
|
||||||
|
# (API endpoint, human-friendly name)
|
||||||
|
'consoleport': ('console-ports', 'Console port'),
|
||||||
|
'consoleserverport': ('console-server-ports', 'Console server port'),
|
||||||
|
'powerport': ('power-ports', 'Power port'),
|
||||||
|
'poweroutlet': ('power-outlets', 'Power outlet'),
|
||||||
|
'interface': ('interfaces', 'Interface'),
|
||||||
|
'frontpanelport': ('front-panel-ports', 'Front panel port'),
|
||||||
|
'rearpanelport': ('rear-panel-ports', 'Rear panel port'),
|
||||||
|
}
|
||||||
|
|
||||||
|
COMPATIBLE_ENDPOINT_TYPES = {
|
||||||
|
'consoleport': ['consoleserverport', 'frontpanelport', 'rearpanelport'],
|
||||||
|
'consoleserverport': ['consoleport', 'frontpanelport', 'rearpanelport'],
|
||||||
|
'powerport': ['poweroutlet'],
|
||||||
|
'poweroutlet': ['powerport'],
|
||||||
|
'interface': ['interface', 'frontpanelport', 'rearpanelport'],
|
||||||
|
'frontpanelport': ['consoleport', 'consoleserverport', 'interface', 'frontpanelport', 'rearpanelport'],
|
||||||
|
'rearpanelport': ['consoleport', 'consoleserverport', 'interface', 'frontpanelport', 'rearpanelport'],
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ import re
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.postgres.forms.array import SimpleArrayField
|
from django.contrib.postgres.forms.array import SimpleArrayField
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
from mptt.forms import TreeNodeChoiceField
|
from mptt.forms import TreeNodeChoiceField
|
||||||
@ -19,11 +20,12 @@ from utilities.forms import (
|
|||||||
BulkEditNullBooleanSelect, ChainedFieldsMixin, ChainedModelChoiceField, CommentField, ComponentForm,
|
BulkEditNullBooleanSelect, ChainedFieldsMixin, ChainedModelChoiceField, CommentField, ComponentForm,
|
||||||
ConfirmationForm, CSVChoiceField, ExpandableNameField, FilterChoiceField, FilterTreeNodeMultipleChoiceField,
|
ConfirmationForm, CSVChoiceField, ExpandableNameField, FilterChoiceField, FilterTreeNodeMultipleChoiceField,
|
||||||
FlexibleModelChoiceField, JSONField, Livesearch, SelectWithDisabled, SelectWithPK, SmallTextarea, SlugField,
|
FlexibleModelChoiceField, JSONField, Livesearch, SelectWithDisabled, SelectWithPK, SmallTextarea, SlugField,
|
||||||
|
ContentTypeSelect
|
||||||
)
|
)
|
||||||
from virtualization.models import Cluster
|
from virtualization.models import Cluster
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .models import (
|
from .models import (
|
||||||
DeviceBay, DeviceBayTemplate, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate,
|
Cable, DeviceBay, DeviceBayTemplate, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate,
|
||||||
Device, DeviceRole, DeviceType, FrontPanelPort, FrontPanelPortTemplate, Interface, InterfaceConnection,
|
Device, DeviceRole, DeviceType, FrontPanelPort, FrontPanelPortTemplate, Interface, InterfaceConnection,
|
||||||
InterfaceTemplate, Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort,
|
InterfaceTemplate, Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort,
|
||||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPanelPort, RearPanelPortTemplate, Region, Site,
|
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPanelPort, RearPanelPortTemplate, Region, Site,
|
||||||
@ -2298,6 +2300,95 @@ class RearPanelPortBulkRenameForm(BulkRenameForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Cables
|
||||||
|
#
|
||||||
|
|
||||||
|
class CableForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
||||||
|
endpoint_b_site = forms.ModelChoiceField(
|
||||||
|
queryset=Site.objects.all(),
|
||||||
|
label='Site',
|
||||||
|
required=False,
|
||||||
|
widget=forms.Select(
|
||||||
|
attrs={'filter-for': 'endpoint_b_rack'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
endpoint_b_rack = ChainedModelChoiceField(
|
||||||
|
queryset=Rack.objects.all(),
|
||||||
|
chains=(
|
||||||
|
('site', 'endpoint_b_site'),
|
||||||
|
),
|
||||||
|
label='Rack',
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/racks/?site_id={{endpoint_b_site}}',
|
||||||
|
attrs={'filter-for': 'endpoint_b_device', 'nullable': 'true'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
endpoint_b_device = ChainedModelChoiceField(
|
||||||
|
queryset=Device.objects.all(),
|
||||||
|
chains=(
|
||||||
|
('site', 'endpoint_b_site'),
|
||||||
|
('rack', 'endpoint_b_rack'),
|
||||||
|
),
|
||||||
|
label='Device',
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/devices/?site_id={{endpoint_b_site}}&rack_id={{endpoint_b_rack}}',
|
||||||
|
display_field='display_name',
|
||||||
|
attrs={'filter-for': 'endpoint_b_id'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
livesearch = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label='Device',
|
||||||
|
widget=Livesearch(
|
||||||
|
query_key='q',
|
||||||
|
query_url='dcim-api:device-list',
|
||||||
|
field_to_update='endpoint_b_device'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
endpoint_b_type = forms.ModelChoiceField(
|
||||||
|
queryset=ContentType.objects.all(),
|
||||||
|
label='Type',
|
||||||
|
widget=ContentTypeSelect(
|
||||||
|
attrs={'filter-for': 'endpoint_b_id'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
endpoint_b_id = forms.ChoiceField(
|
||||||
|
choices=[],
|
||||||
|
label='Name',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/{{endpoint_b_type}}s/?device_id={{endpoint_b_device}}',
|
||||||
|
disabled_indicator='is_connected'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Cable
|
||||||
|
fields = [
|
||||||
|
'endpoint_b_site', 'endpoint_b_rack', 'endpoint_b_device', 'livesearch', 'endpoint_b_type',
|
||||||
|
'endpoint_b_id', 'status', 'label',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CableForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Define available types for endpoint B based on the type of endpoint A
|
||||||
|
endpoint_a_type = self.instance.endpoint_a._meta.model_name
|
||||||
|
self.fields['endpoint_b_type'].queryset = ContentType.objects.filter(
|
||||||
|
model__in=COMPATIBLE_ENDPOINT_TYPES.get(endpoint_a_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
|
||||||
|
# Assign endpoint B
|
||||||
|
cleaned_data = super(CableForm, self).clean()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device bays
|
# Device bays
|
||||||
#
|
#
|
||||||
|
@ -2455,7 +2455,7 @@ class Cable(ChangeLoggedModel):
|
|||||||
"""
|
"""
|
||||||
endpoint_a_type = models.ForeignKey(
|
endpoint_a_type = models.ForeignKey(
|
||||||
to=ContentType,
|
to=ContentType,
|
||||||
limit_choices_to={'model__in': CABLE_CONNECTION_TYPES},
|
limit_choices_to={'model__in': CABLE_ENDPOINT_TYPES},
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='+'
|
related_name='+'
|
||||||
)
|
)
|
||||||
@ -2466,7 +2466,7 @@ class Cable(ChangeLoggedModel):
|
|||||||
)
|
)
|
||||||
endpoint_b_type = models.ForeignKey(
|
endpoint_b_type = models.ForeignKey(
|
||||||
to=ContentType,
|
to=ContentType,
|
||||||
limit_choices_to={'model__in': CABLE_CONNECTION_TYPES},
|
limit_choices_to={'model__in': CABLE_ENDPOINT_TYPES},
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='+'
|
related_name='+'
|
||||||
)
|
)
|
||||||
|
@ -161,7 +161,8 @@ urlpatterns = [
|
|||||||
url(r'^devices/console-ports/add/$', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
|
url(r'^devices/console-ports/add/$', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
|
||||||
url(r'^devices/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
|
url(r'^devices/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
||||||
url(r'^console-ports/(?P<pk>\d+)/connect/$', views.ConsolePortConnectView.as_view(), name='consoleport_connect'),
|
# url(r'^console-ports/(?P<pk>\d+)/connect/$', views.ConsolePortConnectView.as_view(), name='consoleport_connect'),
|
||||||
|
url(r'^console-ports/(?P<endpoint_a_id>\d+)/connect/$', views.CableConnectView.as_view(), name='consoleport_connect', kwargs={'endpoint_a_type': 'consoleport'}),
|
||||||
url(r'^console-ports/(?P<pk>\d+)/disconnect/$', views.ConsolePortDisconnectView.as_view(), name='consoleport_disconnect'),
|
url(r'^console-ports/(?P<pk>\d+)/disconnect/$', views.ConsolePortDisconnectView.as_view(), name='consoleport_disconnect'),
|
||||||
url(r'^console-ports/(?P<pk>\d+)/edit/$', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
|
url(r'^console-ports/(?P<pk>\d+)/edit/$', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
|
||||||
url(r'^console-ports/(?P<pk>\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
url(r'^console-ports/(?P<pk>\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
||||||
@ -171,7 +172,8 @@ urlpatterns = [
|
|||||||
url(r'^devices/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
|
url(r'^devices/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/console-server-ports/disconnect/$', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
|
url(r'^devices/(?P<pk>\d+)/console-server-ports/disconnect/$', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
|
||||||
url(r'^devices/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
||||||
url(r'^console-server-ports/(?P<pk>\d+)/connect/$', views.ConsoleServerPortConnectView.as_view(), name='consoleserverport_connect'),
|
# url(r'^console-server-ports/(?P<pk>\d+)/connect/$', views.ConsoleServerPortConnectView.as_view(), name='consoleserverport_connect'),
|
||||||
|
url(r'^console-server-ports/(?P<endpoint_a_id>\d+)/connect/$', views.CableConnectView.as_view(), name='consoleserverport_connect', kwargs={'endpoint_a_type': 'consoleserverport'}),
|
||||||
url(r'^console-server-ports/(?P<pk>\d+)/disconnect/$', views.ConsoleServerPortDisconnectView.as_view(), name='consoleserverport_disconnect'),
|
url(r'^console-server-ports/(?P<pk>\d+)/disconnect/$', views.ConsoleServerPortDisconnectView.as_view(), name='consoleserverport_disconnect'),
|
||||||
url(r'^console-server-ports/(?P<pk>\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
|
url(r'^console-server-ports/(?P<pk>\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
|
||||||
url(r'^console-server-ports/(?P<pk>\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
url(r'^console-server-ports/(?P<pk>\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
||||||
@ -181,7 +183,8 @@ urlpatterns = [
|
|||||||
url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
|
url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
|
||||||
url(r'^devices/(?P<pk>\d+)/power-ports/add/$', views.PowerPortCreateView.as_view(), name='powerport_add'),
|
url(r'^devices/(?P<pk>\d+)/power-ports/add/$', views.PowerPortCreateView.as_view(), name='powerport_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
||||||
url(r'^power-ports/(?P<pk>\d+)/connect/$', views.PowerPortConnectView.as_view(), name='powerport_connect'),
|
# url(r'^power-ports/(?P<pk>\d+)/connect/$', views.PowerPortConnectView.as_view(), name='powerport_connect'),
|
||||||
|
url(r'^power-ports/(?P<endpoint_a_id>\d+)/connect/$', views.CableConnectView.as_view(), name='powerport_connect', kwargs={'endpoint_a_type': 'powerport'}),
|
||||||
url(r'^power-ports/(?P<pk>\d+)/disconnect/$', views.PowerPortDisconnectView.as_view(), name='powerport_disconnect'),
|
url(r'^power-ports/(?P<pk>\d+)/disconnect/$', views.PowerPortDisconnectView.as_view(), name='powerport_disconnect'),
|
||||||
url(r'^power-ports/(?P<pk>\d+)/edit/$', views.PowerPortEditView.as_view(), name='powerport_edit'),
|
url(r'^power-ports/(?P<pk>\d+)/edit/$', views.PowerPortEditView.as_view(), name='powerport_edit'),
|
||||||
url(r'^power-ports/(?P<pk>\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
url(r'^power-ports/(?P<pk>\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
||||||
@ -191,7 +194,8 @@ urlpatterns = [
|
|||||||
url(r'^devices/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
|
url(r'^devices/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/power-outlets/disconnect/$', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
|
url(r'^devices/(?P<pk>\d+)/power-outlets/disconnect/$', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
|
||||||
url(r'^devices/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
||||||
url(r'^power-outlets/(?P<pk>\d+)/connect/$', views.PowerOutletConnectView.as_view(), name='poweroutlet_connect'),
|
# url(r'^power-outlets/(?P<pk>\d+)/connect/$', views.PowerOutletConnectView.as_view(), name='poweroutlet_connect'),
|
||||||
|
url(r'^power-outlets/(?P<endpoint_a_id>\d+)/connect/$', views.CableConnectView.as_view(), name='poweroutlet_connect', kwargs={'endpoint_a_type': 'poweroutlet'}),
|
||||||
url(r'^power-outlets/(?P<pk>\d+)/disconnect/$', views.PowerOutletDisconnectView.as_view(), name='poweroutlet_disconnect'),
|
url(r'^power-outlets/(?P<pk>\d+)/disconnect/$', views.PowerOutletDisconnectView.as_view(), name='poweroutlet_disconnect'),
|
||||||
url(r'^power-outlets/(?P<pk>\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
url(r'^power-outlets/(?P<pk>\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
||||||
url(r'^power-outlets/(?P<pk>\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
|
url(r'^power-outlets/(?P<pk>\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.paginator import EmptyPage, PageNotAnInteger
|
from django.core.paginator import EmptyPage, PageNotAnInteger
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
from django.forms import modelformset_factory
|
from django.forms import modelformset_factory
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import Http404, HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
@ -30,7 +32,7 @@ from virtualization.models import VirtualMachine
|
|||||||
from . import filters, forms, tables
|
from . import filters, forms, tables
|
||||||
from .constants import CONNECTION_STATUS_CONNECTED
|
from .constants import CONNECTION_STATUS_CONNECTED
|
||||||
from .models import (
|
from .models import (
|
||||||
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPanelPort, FrontPanelPortTemplate, Interface, InterfaceConnection,
|
DeviceBayTemplate, DeviceRole, DeviceType, FrontPanelPort, FrontPanelPortTemplate, Interface, InterfaceConnection,
|
||||||
InterfaceTemplate, Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort,
|
InterfaceTemplate, Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort,
|
||||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPanelPort, RearPanelPortTemplate, Region, Site,
|
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPanelPort, RearPanelPortTemplate, Region, Site,
|
||||||
@ -2152,6 +2154,27 @@ class InterfaceConnectionsListView(ObjectListView):
|
|||||||
template_name = 'dcim/interface_connections_list.html'
|
template_name = 'dcim/interface_connections_list.html'
|
||||||
|
|
||||||
|
|
||||||
|
class CableConnectView(PermissionRequiredMixin, ObjectEditView):
|
||||||
|
permission_required = 'dcim.add_cable'
|
||||||
|
model = Cable
|
||||||
|
model_form = forms.CableForm
|
||||||
|
template_name = 'dcim/cable_connect.html'
|
||||||
|
|
||||||
|
def alter_obj(self, obj, request, url_args, url_kwargs):
|
||||||
|
# Retrieve endpoint A based on the given type and PK
|
||||||
|
endpoint_a_type = url_kwargs.get('endpoint_a_type')
|
||||||
|
endpoint_a_id = url_kwargs.get('endpoint_a_id')
|
||||||
|
try:
|
||||||
|
model = apps.get_model(
|
||||||
|
app_label='dcim',
|
||||||
|
model_name=endpoint_a_type
|
||||||
|
)
|
||||||
|
obj.endpoint_a = model.objects.get(pk=endpoint_a_id)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
raise Http404
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Inventory items
|
# Inventory items
|
||||||
#
|
#
|
||||||
|
@ -93,7 +93,10 @@ $(document).ready(function() {
|
|||||||
var rendered_url = api_url;
|
var rendered_url = api_url;
|
||||||
while (match = filter_regex.exec(api_url)) {
|
while (match = filter_regex.exec(api_url)) {
|
||||||
var filter_field = $('#id_' + match[1]);
|
var filter_field = $('#id_' + match[1]);
|
||||||
if (filter_field.val()) {
|
var custom_attr = $('option:selected', filter_field).attr('api-value');
|
||||||
|
if (custom_attr) {
|
||||||
|
rendered_url = rendered_url.replace(match[0], custom_attr);
|
||||||
|
} else if (filter_field.val()) {
|
||||||
rendered_url = rendered_url.replace(match[0], filter_field.val());
|
rendered_url = rendered_url.replace(match[0], filter_field.val());
|
||||||
} else if (filter_field.attr('nullable') == 'true') {
|
} else if (filter_field.attr('nullable') == 'true') {
|
||||||
rendered_url = rendered_url.replace(match[0], '0');
|
rendered_url = rendered_url.replace(match[0], '0');
|
||||||
|
106
netbox/templates/dcim/cable_connect.html
Normal file
106
netbox/templates/dcim/cable_connect.html
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
{% extends '_base.html' %}
|
||||||
|
{% load static from staticfiles %}
|
||||||
|
{% load form_helpers %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form action="." method="post" class="form form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for field in form.hidden_fields %}
|
||||||
|
{{ field }}
|
||||||
|
{% endfor %}
|
||||||
|
{% if form.non_field_errors %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-md-offset-3">
|
||||||
|
<div class="panel panel-danger">
|
||||||
|
<div class="panel-heading"><strong>Errors</strong></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% with endpoint_a=form.instance.endpoint_a %}
|
||||||
|
<h3>{% block title %}Connect {{ endpoint_a.device }} {{ endpoint_a }}{% endblock %}</h3>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading text-center">
|
||||||
|
<strong>A Side</strong>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label required">Site</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<p class="form-control-static">{{ endpoint_a.device.site }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label required">Rack</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<p class="form-control-static">{{ endpoint_a.device.rack|default:"None" }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label required">Device</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<p class="form-control-static">{{ endpoint_a.device }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label required">Name</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<p class="form-control-static">{{ endpoint_a }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 text-center" style="padding-top: 90px;">
|
||||||
|
<i class="fa fa-exchange fa-4x"></i>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading text-center">
|
||||||
|
<strong>B Side</strong>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
|
<li role="presentation" class="active"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>
|
||||||
|
<li role="presentation"><a href="#select" aria-controls="home" role="tab" data-toggle="tab">Select</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active" id="search">
|
||||||
|
{% render_field form.livesearch %}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane" id="select">
|
||||||
|
{% render_field form.endpoint_b_site %}
|
||||||
|
{% render_field form.endpoint_b_rack %}
|
||||||
|
{% render_field form.endpoint_b_device %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% render_field form.endpoint_b_type %}
|
||||||
|
{% render_field form.endpoint_b_id %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 col-md-offset-4">
|
||||||
|
{% render_field form.status %}
|
||||||
|
{% render_field form.label %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-12 text-center">
|
||||||
|
<button type="submit" name="_update" class="btn btn-primary">Connect</button>
|
||||||
|
<a href="{{ return_url }}" class="btn btn-default">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascript %}
|
||||||
|
<script src="{% static 'js/livesearch.js' %}?v{{ settings.VERSION }}"></script>
|
||||||
|
{% endblock %}
|
@ -30,7 +30,7 @@
|
|||||||
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'dcim:consoleport_connect' pk=cp.pk %}" title="Connect" class="btn btn-success btn-xs">
|
<a href="{% url 'dcim:consoleport_connect' endpoint_a_id=cp.pk %}" title="Connect" class="btn btn-success btn-xs">
|
||||||
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'dcim:consoleserverport_connect' pk=csp.pk %}" title="Connect" class="btn btn-success btn-xs">
|
<a href="{% url 'dcim:consoleserverport_connect' endpoint_a_id=csp.pk %}" title="Connect" class="btn btn-success btn-xs">
|
||||||
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'dcim:poweroutlet_connect' pk=po.pk %}" title="Connect" class="btn btn-success btn-xs">
|
<a href="{% url 'dcim:poweroutlet_connect' endpoint_a_id=po.pk %}" title="Connect" class="btn btn-success btn-xs">
|
||||||
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-full" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'dcim:powerport_connect' pk=pp.pk %}" title="Connect" class="btn btn-success btn-xs">
|
<a href="{% url 'dcim:powerport_connect' endpoint_a_id=pp.pk %}" title="Connect" class="btn btn-success btn-xs">
|
||||||
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -207,6 +207,15 @@ class SelectWithPK(forms.Select):
|
|||||||
option_template_name = 'widgets/select_option_with_pk.html'
|
option_template_name = 'widgets/select_option_with_pk.html'
|
||||||
|
|
||||||
|
|
||||||
|
class ContentTypeSelect(forms.Select):
|
||||||
|
"""
|
||||||
|
Appends an `api-value` attribute equal to the slugified model name for each ContentType. For example:
|
||||||
|
<option value="37" api-value="console-server-port">console server port</option>
|
||||||
|
This attribute can be used to reference the relevant API endpoint for a particular ContentType.
|
||||||
|
"""
|
||||||
|
option_template_name = 'widgets/select_contenttype.html'
|
||||||
|
|
||||||
|
|
||||||
class ArrayFieldSelectMultiple(SelectWithDisabled, forms.SelectMultiple):
|
class ArrayFieldSelectMultiple(SelectWithDisabled, forms.SelectMultiple):
|
||||||
"""
|
"""
|
||||||
MultiSelect widget for a SimpleArrayField. Choices must be populated on the widget.
|
MultiSelect widget for a SimpleArrayField. Choices must be populated on the widget.
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<option value="{{ widget.value }}"{% include "django/forms/widgets/attrs.html" %}{% if widget.value %} api-value="{{ widget.label|slugify }}"{% endif %}>{{ widget.label.label|default:widget.label }}</option>
|
Loading…
Reference in New Issue
Block a user