mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 17:08:41 -06:00
Further work on power feed modeling
This commit is contained in:
parent
705f82e416
commit
681e20133a
@ -496,7 +496,7 @@ class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
|
|||||||
queryset = PowerPort.objects.select_related(
|
queryset = PowerPort.objects.select_related(
|
||||||
'device', 'connected_endpoint__device'
|
'device', 'connected_endpoint__device'
|
||||||
).filter(
|
).filter(
|
||||||
connected_endpoint__isnull=False
|
_connected_poweroutlet__isnull=False
|
||||||
)
|
)
|
||||||
serializer_class = serializers.PowerPortSerializer
|
serializer_class = serializers.PowerPortSerializer
|
||||||
filterset_class = filters.PowerConnectionFilter
|
filterset_class = filters.PowerConnectionFilter
|
||||||
|
@ -420,7 +420,7 @@ CABLE_TERMINATION_TYPE_CHOICES = {
|
|||||||
COMPATIBLE_TERMINATION_TYPES = {
|
COMPATIBLE_TERMINATION_TYPES = {
|
||||||
'consoleport': ['consoleserverport', 'frontport', 'rearport'],
|
'consoleport': ['consoleserverport', 'frontport', 'rearport'],
|
||||||
'consoleserverport': ['consoleport', 'frontport', 'rearport'],
|
'consoleserverport': ['consoleport', 'frontport', 'rearport'],
|
||||||
'powerport': ['poweroutlet'],
|
'powerport': ['poweroutlet', 'powerfeed'],
|
||||||
'poweroutlet': ['powerport'],
|
'poweroutlet': ['powerport'],
|
||||||
'interface': ['interface', 'circuittermination', 'frontport', 'rearport'],
|
'interface': ['interface', 'circuittermination', 'frontport', 'rearport'],
|
||||||
'frontport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport', 'circuittermination'],
|
'frontport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport', 'circuittermination'],
|
||||||
|
@ -10,6 +10,7 @@ from mptt.forms import TreeNodeChoiceField
|
|||||||
from taggit.forms import TagField
|
from taggit.forms import TagField
|
||||||
from timezone_field import TimeZoneFormField
|
from timezone_field import TimeZoneFormField
|
||||||
|
|
||||||
|
from circuits.models import Circuit, CircuitTermination, Provider
|
||||||
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
||||||
from ipam.models import IPAddress, VLAN, VLANGroup
|
from ipam.models import IPAddress, VLAN, VLANGroup
|
||||||
from tenancy.forms import TenancyForm
|
from tenancy.forms import TenancyForm
|
||||||
@ -2521,7 +2522,7 @@ class RearPortBulkDisconnectForm(ConfirmationForm):
|
|||||||
# Cables
|
# Cables
|
||||||
#
|
#
|
||||||
|
|
||||||
class CableCreateForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
class ConnectCableToDeviceForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
||||||
termination_b_site = forms.ModelChoiceField(
|
termination_b_site = forms.ModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site',
|
label='Site',
|
||||||
@ -2602,6 +2603,104 @@ class CableCreateForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectCableToCircuitForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
||||||
|
termination_b_provider = forms.ModelChoiceField(
|
||||||
|
queryset=Provider.objects.all(),
|
||||||
|
label='Provider',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/circuits/providers/',
|
||||||
|
filter_for={
|
||||||
|
'termination_b_circuit': 'provider_id',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
termination_b_circuit = ChainedModelChoiceField(
|
||||||
|
queryset=Circuit.objects.all(),
|
||||||
|
chains=(
|
||||||
|
('provider', 'termination_b_provider'),
|
||||||
|
),
|
||||||
|
label='Circuit',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/circuits/circuits/',
|
||||||
|
display_field='cid',
|
||||||
|
filter_for={
|
||||||
|
'termination_b_id': 'circuit_id',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
termination_b_id = forms.IntegerField(
|
||||||
|
label='Termination',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/circuits/circuit-terminations/',
|
||||||
|
disabled_indicator='cable'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Cable
|
||||||
|
fields = [
|
||||||
|
'termination_b_provider', 'termination_b_circuit', 'termination_b_id', 'type', 'status', 'label', 'color',
|
||||||
|
'length', 'length_unit',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectCableToPowerFeedForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
||||||
|
termination_b_site = forms.ModelChoiceField(
|
||||||
|
queryset=Site.objects.all(),
|
||||||
|
label='Site',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/sites/',
|
||||||
|
display_field='cid',
|
||||||
|
filter_for={
|
||||||
|
'termination_b_rackgroup': 'site_id',
|
||||||
|
'termination_b_powerpanel': 'site_id',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
termination_b_rackgroup = ChainedModelChoiceField(
|
||||||
|
queryset=RackGroup.objects.all(),
|
||||||
|
label='Rack Group',
|
||||||
|
chains=(
|
||||||
|
('site', 'termination_b_site'),
|
||||||
|
),
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/rack-groups/',
|
||||||
|
display_field='cid',
|
||||||
|
filter_for={
|
||||||
|
'termination_b_powerpanel': 'rackgroup_id',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
termination_b_powerpanel = ChainedModelChoiceField(
|
||||||
|
queryset=PowerPanel.objects.all(),
|
||||||
|
chains=(
|
||||||
|
('site', 'termination_b_site'),
|
||||||
|
('rack_group', 'termination_b_rackgroup'),
|
||||||
|
),
|
||||||
|
label='Power Panel',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/power-panels/',
|
||||||
|
filter_for={
|
||||||
|
'termination_b_powerfeed': 'powerpanel_id',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
termination_b_id = forms.IntegerField(
|
||||||
|
label='Power Feed',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url='/api/dcim/power-feeds/',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Cable
|
||||||
|
fields = [
|
||||||
|
'termination_b_rackgroup', 'termination_b_powerpanel', 'termination_b_id', 'type', 'status', 'label',
|
||||||
|
'color', 'length', 'length_unit',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class CableForm(BootstrapMixin, forms.ModelForm):
|
class CableForm(BootstrapMixin, forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 2.1.7 on 2019-03-12 14:08
|
# Generated by Django 2.1.7 on 2019-03-21 20:59
|
||||||
|
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
@ -21,14 +21,15 @@ class Migration(migrations.Migration):
|
|||||||
('created', models.DateField(auto_now_add=True, null=True)),
|
('created', models.DateField(auto_now_add=True, null=True)),
|
||||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||||
('name', models.CharField(max_length=50)),
|
('name', models.CharField(max_length=50)),
|
||||||
('type', models.PositiveSmallIntegerField(default=1)),
|
|
||||||
('status', models.PositiveSmallIntegerField(default=1)),
|
('status', models.PositiveSmallIntegerField(default=1)),
|
||||||
|
('type', models.PositiveSmallIntegerField(default=1)),
|
||||||
('supply', models.PositiveSmallIntegerField(default=1)),
|
('supply', models.PositiveSmallIntegerField(default=1)),
|
||||||
|
('phase', models.PositiveSmallIntegerField(default=1)),
|
||||||
('voltage', models.PositiveSmallIntegerField(default=120, validators=[django.core.validators.MinValueValidator(1)])),
|
('voltage', models.PositiveSmallIntegerField(default=120, validators=[django.core.validators.MinValueValidator(1)])),
|
||||||
('amperage', models.PositiveSmallIntegerField(default=20, validators=[django.core.validators.MinValueValidator(1)])),
|
('amperage', models.PositiveSmallIntegerField(default=20, validators=[django.core.validators.MinValueValidator(1)])),
|
||||||
('phase', models.PositiveSmallIntegerField(default=1)),
|
|
||||||
('max_utilization', models.PositiveSmallIntegerField(default=80, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
|
('max_utilization', models.PositiveSmallIntegerField(default=80, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
|
||||||
('comments', models.TextField(blank=True)),
|
('comments', models.TextField(blank=True)),
|
||||||
|
('cable', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.Cable')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['power_panel', 'name'],
|
'ordering': ['power_panel', 'name'],
|
||||||
@ -63,6 +64,26 @@ class Migration(migrations.Migration):
|
|||||||
name='tags',
|
name='tags',
|
||||||
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='powerfeed',
|
||||||
|
name='connected_endpoint',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.PowerPort'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='powerfeed',
|
||||||
|
name='connection_status',
|
||||||
|
field=models.NullBooleanField(),
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='powerport',
|
||||||
|
old_name='connected_endpoint',
|
||||||
|
new_name='_connected_poweroutlet',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='powerport',
|
||||||
|
name='_connected_powerfeed',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.PowerFeed'),
|
||||||
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='powerpanel',
|
name='powerpanel',
|
||||||
unique_together={('site', 'name')},
|
unique_together={('site', 'name')},
|
||||||
|
@ -1828,13 +1828,20 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
connected_endpoint = models.OneToOneField(
|
_connected_poweroutlet = models.OneToOneField(
|
||||||
to='dcim.PowerOutlet',
|
to='dcim.PowerOutlet',
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
related_name='connected_endpoint',
|
related_name='connected_endpoint',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
_connected_powerfeed = models.OneToOneField(
|
||||||
|
to='dcim.PowerFeed',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name='+',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
connection_status = models.NullBooleanField(
|
connection_status = models.NullBooleanField(
|
||||||
choices=CONNECTION_STATUS_CHOICES,
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
blank=True
|
blank=True
|
||||||
@ -1862,6 +1869,28 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
self.description,
|
self.description,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connected_endpoint(self):
|
||||||
|
if self._connected_poweroutlet:
|
||||||
|
return self._connected_poweroutlet
|
||||||
|
return self._connected_powerfeed
|
||||||
|
|
||||||
|
@connected_endpoint.setter
|
||||||
|
def connected_endpoint(self, value):
|
||||||
|
if value is None:
|
||||||
|
self._connected_poweroutlet = None
|
||||||
|
self._connected_powerfeed = None
|
||||||
|
elif isinstance(value, PowerOutlet):
|
||||||
|
self._connected_poweroutlet = value
|
||||||
|
self._connected_powerfeed = None
|
||||||
|
elif isinstance(value, PowerFeed):
|
||||||
|
self._connected_poweroutlet = None
|
||||||
|
self._connected_powerfeed = value
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
"Connected endpoint must be a PowerOutlet or PowerFeed, not {}.".format(type(value))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Power outlets
|
# Power outlets
|
||||||
@ -2646,6 +2675,14 @@ class Cable(ChangeLoggedModel):
|
|||||||
def get_status_class(self):
|
def get_status_class(self):
|
||||||
return 'success' if self.status else 'info'
|
return 'success' if self.status else 'info'
|
||||||
|
|
||||||
|
def get_compatible_types(self):
|
||||||
|
"""
|
||||||
|
Return all termination types compatible with termination A.
|
||||||
|
"""
|
||||||
|
if self.termination_a is None:
|
||||||
|
return
|
||||||
|
return COMPATIBLE_TERMINATION_TYPES[self.termination_a._meta.model_name]
|
||||||
|
|
||||||
def get_path_endpoints(self):
|
def get_path_endpoints(self):
|
||||||
"""
|
"""
|
||||||
Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be
|
Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be
|
||||||
@ -2712,7 +2749,7 @@ class PowerPanel(ChangeLoggedModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PowerFeed(ChangeLoggedModel, CustomFieldModel):
|
class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
|
||||||
"""
|
"""
|
||||||
An electrical circuit delivered from a PowerPanel.
|
An electrical circuit delivered from a PowerPanel.
|
||||||
"""
|
"""
|
||||||
@ -2727,6 +2764,17 @@ class PowerFeed(ChangeLoggedModel, CustomFieldModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
connected_endpoint = models.OneToOneField(
|
||||||
|
to='dcim.PowerPort',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name='+',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
connection_status = models.NullBooleanField(
|
||||||
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ import re
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
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.contrib.contenttypes.models import ContentType
|
||||||
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, F
|
from django.db.models import Count, F
|
||||||
@ -10,10 +11,11 @@ from django.forms import modelformset_factory
|
|||||||
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
|
||||||
|
from django.utils.http import is_safe_url
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
from circuits.models import Circuit
|
from circuits.models import Circuit, CircuitTermination
|
||||||
from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
|
from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
|
||||||
from extras.views import ObjectConfigContextView
|
from extras.views import ObjectConfigContextView
|
||||||
from ipam.models import Prefix, VLAN
|
from ipam.models import Prefix, VLAN
|
||||||
@ -913,7 +915,7 @@ class DeviceView(View):
|
|||||||
consoleserverports = device.consoleserverports.select_related('connected_endpoint__device', 'cable')
|
consoleserverports = device.consoleserverports.select_related('connected_endpoint__device', 'cable')
|
||||||
|
|
||||||
# Power ports
|
# Power ports
|
||||||
power_ports = device.powerports.select_related('connected_endpoint__device', 'cable')
|
power_ports = device.powerports.select_related('_connected_poweroutlet__device', 'cable')
|
||||||
|
|
||||||
# Power outlets
|
# Power outlets
|
||||||
poweroutlets = device.poweroutlets.select_related('connected_endpoint__device', 'cable')
|
poweroutlets = device.poweroutlets.select_related('connected_endpoint__device', 'cable')
|
||||||
@ -1673,20 +1675,76 @@ class CableTraceView(View):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class CableCreateView(PermissionRequiredMixin, ObjectEditView):
|
class CableCreateView(PermissionRequiredMixin, GetReturnURLMixin, View):
|
||||||
permission_required = 'dcim.add_cable'
|
permission_required = 'dcim.add_cable'
|
||||||
model = Cable
|
|
||||||
model_form = forms.CableCreateForm
|
|
||||||
template_name = 'dcim/cable_connect.html'
|
template_name = 'dcim/cable_connect.html'
|
||||||
|
|
||||||
def alter_obj(self, obj, request, url_args, url_kwargs):
|
def _get_form_class(self):
|
||||||
|
if self.termination_b_type == 'circuit':
|
||||||
|
return forms.ConnectCableToCircuitForm
|
||||||
|
if self.termination_b_type == 'powerfeed':
|
||||||
|
return forms.ConnectCableToPowerFeedForm
|
||||||
|
return forms.ConnectCableToDeviceForm
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
|
||||||
# Retrieve endpoint A based on the given type and PK
|
# Retrieve endpoint A based on the given type and PK
|
||||||
termination_a_type = url_kwargs.get('termination_a_type')
|
termination_a_type = kwargs.get('termination_a_type')
|
||||||
termination_a_id = url_kwargs.get('termination_a_id')
|
termination_a_id = kwargs.get('termination_a_id')
|
||||||
obj.termination_a = termination_a_type.objects.get(pk=termination_a_id)
|
self.obj = Cable(
|
||||||
|
termination_a=termination_a_type.objects.get(pk=termination_a_id)
|
||||||
|
)
|
||||||
|
|
||||||
return obj
|
self.termination_b_type = request.GET.get('type')
|
||||||
|
if self.termination_b_type == 'circuit':
|
||||||
|
self.obj.termination_b_type = ContentType.objects.get_for_model(CircuitTermination)
|
||||||
|
elif self.termination_b_type == 'powerfeed':
|
||||||
|
self.obj.termination_b_type = ContentType.objects.get_for_model(PowerFeed)
|
||||||
|
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
# Parse initial data manually to avoid setting field values as lists
|
||||||
|
initial_data = {k: request.GET[k] for k in request.GET}
|
||||||
|
|
||||||
|
form = self._get_form_class()(instance=self.obj, initial=initial_data)
|
||||||
|
|
||||||
|
return render(request, self.template_name, {
|
||||||
|
'obj': self.obj,
|
||||||
|
'obj_type': Cable._meta.verbose_name,
|
||||||
|
'form': form,
|
||||||
|
'return_url': self.get_return_url(request, self.obj),
|
||||||
|
})
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
form = self._get_form_class()(request.POST, request.FILES, instance=self.obj)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
obj = form.save()
|
||||||
|
|
||||||
|
msg = 'Created cable <a href="{}">{}</a>'.format(
|
||||||
|
obj.get_absolute_url(),
|
||||||
|
escape(obj)
|
||||||
|
)
|
||||||
|
messages.success(request, mark_safe(msg))
|
||||||
|
|
||||||
|
if '_addanother' in request.POST:
|
||||||
|
return redirect(request.get_full_path())
|
||||||
|
|
||||||
|
return_url = form.cleaned_data.get('return_url')
|
||||||
|
if return_url is not None and is_safe_url(url=return_url, allowed_hosts=request.get_host()):
|
||||||
|
return redirect(return_url)
|
||||||
|
else:
|
||||||
|
return redirect(self.get_return_url(request, obj))
|
||||||
|
|
||||||
|
return render(request, self.template_name, {
|
||||||
|
'obj': self.obj,
|
||||||
|
'obj_type': Cable._meta.verbose_name,
|
||||||
|
'form': form,
|
||||||
|
'return_url': self.get_return_url(request, self.obj),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class CableEditView(PermissionRequiredMixin, ObjectEditView):
|
class CableEditView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -1763,11 +1821,11 @@ class ConsoleConnectionsListView(ObjectListView):
|
|||||||
|
|
||||||
class PowerConnectionsListView(ObjectListView):
|
class PowerConnectionsListView(ObjectListView):
|
||||||
queryset = PowerPort.objects.select_related(
|
queryset = PowerPort.objects.select_related(
|
||||||
'device', 'connected_endpoint__device'
|
'device', '_connected_poweroutlet__device'
|
||||||
).filter(
|
).filter(
|
||||||
connected_endpoint__isnull=False
|
_connected_poweroutlet__isnull=False
|
||||||
).order_by(
|
).order_by(
|
||||||
'cable', 'connected_endpoint__device__name', 'connected_endpoint__name'
|
'cable', '_connected_poweroutlet__device__name', '_connected_poweroutlet__name'
|
||||||
)
|
)
|
||||||
filter = filters.PowerConnectionFilter
|
filter = filters.PowerConnectionFilter
|
||||||
filter_form = forms.PowerConnectionFilterForm
|
filter_form = forms.PowerConnectionFilterForm
|
||||||
|
@ -541,7 +541,7 @@ class TopologyMap(models.Model):
|
|||||||
from dcim.models import PowerPort
|
from dcim.models import PowerPort
|
||||||
|
|
||||||
# Add all power connections to the graph
|
# Add all power connections to the graph
|
||||||
for pp in PowerPort.objects.filter(device__in=devices, connected_endpoint__device__in=devices):
|
for pp in PowerPort.objects.filter(device__in=devices, _connected_poweroutlet__device__in=devices):
|
||||||
style = 'solid' if pp.connection_status == CONNECTION_STATUS_CONNECTED else 'dashed'
|
style = 'solid' if pp.connection_status == CONNECTION_STATUS_CONNECTED else 'dashed'
|
||||||
self.graph.edge(pp.connected_endpoint.device.name, pp.device.name, style=style)
|
self.graph.edge(pp.connected_endpoint.device.name, pp.device.name, style=style)
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ class HomeView(View):
|
|||||||
connected_endpoint__isnull=False
|
connected_endpoint__isnull=False
|
||||||
)
|
)
|
||||||
connected_powerports = PowerPort.objects.filter(
|
connected_powerports = PowerPort.objects.filter(
|
||||||
connected_endpoint__isnull=False
|
_connected_poweroutlet__isnull=False
|
||||||
)
|
)
|
||||||
connected_interfaces = Interface.objects.filter(
|
connected_interfaces = Interface.objects.filter(
|
||||||
_connected_interface__isnull=False,
|
_connected_interface__isnull=False,
|
||||||
|
@ -101,21 +101,34 @@
|
|||||||
<strong>B Side</strong>
|
<strong>B Side</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
{# TODO: Clean this up #}
|
||||||
<li role="presentation" class="active"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>
|
{% if 'termination_b_site' in form.fields %}
|
||||||
<li role="presentation"><a href="#select" aria-controls="home" role="tab" data-toggle="tab">Select</a></li>
|
{% render_field form.termination_b_site %}
|
||||||
</ul>
|
{% endif %}
|
||||||
<div class="tab-content">
|
{% if 'termination_b_rackgroup' in form.fields %}
|
||||||
<div class="tab-pane active" id="search">
|
{% render_field form.termination_b_rackgroup %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
{% if 'termination_b_rack' in form.fields %}
|
||||||
<div class="tab-pane" id="select">
|
{% render_field form.termination_b_rack %}
|
||||||
{% render_field form.termination_b_site %}
|
{% endif %}
|
||||||
{% render_field form.termination_b_rack %}
|
{% if 'termination_b_device' in form.fields %}
|
||||||
</div>
|
{% render_field form.termination_b_device %}
|
||||||
</div>
|
{% endif %}
|
||||||
{% render_field form.termination_b_device %}
|
{% if 'termination_b_type' in form.fields %}
|
||||||
{% render_field form.termination_b_type %}
|
{% render_field form.termination_b_type %}
|
||||||
|
{% endif %}
|
||||||
|
{% if 'termination_b_provider' in form.fields %}
|
||||||
|
{% render_field form.termination_b_provider %}
|
||||||
|
{% endif %}
|
||||||
|
{% if 'termination_b_circuit' in form.fields %}
|
||||||
|
{% render_field form.termination_b_circuit %}
|
||||||
|
{% endif %}
|
||||||
|
{% if 'termination_b_powerpanel' in form.fields %}
|
||||||
|
{% render_field form.termination_b_powerpanel %}
|
||||||
|
{% endif %}
|
||||||
|
{% if 'termination_b_powerfeed' in form.fields %}
|
||||||
|
{% render_field form.termination_b_powerfeed %}
|
||||||
|
{% endif %}
|
||||||
{% render_field form.termination_b_id %}
|
{% render_field form.termination_b_id %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,13 +20,17 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
{# Connection #}
|
{# Connection #}
|
||||||
{% if pp.connected_endpoint %}
|
{% if pp.connected_endpoint.device %}
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url 'dcim:device' pk=pp.connected_endpoint.device.pk %}">{{ pp.connected_endpoint.device }}</a>
|
<a href="{% url 'dcim:device' pk=pp.connected_endpoint.device.pk %}">{{ pp.connected_endpoint.device }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ pp.connected_endpoint }}
|
{{ pp.connected_endpoint }}
|
||||||
</td>
|
</td>
|
||||||
|
{% elif pp.connected_endpoint %}
|
||||||
|
<td colspan="2">
|
||||||
|
<a href="{{ pp.connected_endpoint.get_absolute_url }}">{{ pp.connected_endpoint }}</a>
|
||||||
|
</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<span class="text-muted">Not connected</span>
|
<span class="text-muted">Not connected</span>
|
||||||
|
Loading…
Reference in New Issue
Block a user