mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Fixes #2567: Introduced proxy models to represent console/power/interface connections
This commit is contained in:
@ -40,6 +40,7 @@ NetBox now supports modeling physical cables for console, power, and interface c
|
||||
## Bug Fixes (from v2.5-beta1)
|
||||
|
||||
* [#2563](https://github.com/digitalocean/netbox/issues/2563) - Enable export templates for cables
|
||||
* [#2567](https://github.com/digitalocean/netbox/issues/2567) - Introduced proxy models to represent console/power/interface connections
|
||||
|
||||
## API Changes
|
||||
|
||||
|
@ -1698,7 +1698,7 @@ class ConsolePort(CableTermination, ComponentModel):
|
||||
objects = DeviceComponentManager()
|
||||
tags = TaggableManager()
|
||||
|
||||
csv_headers = ['console_server', 'connected_endpoint', 'device', 'console_port', 'connection_status']
|
||||
csv_headers = ['device', 'name']
|
||||
|
||||
class Meta:
|
||||
ordering = ['device', 'name']
|
||||
@ -1712,11 +1712,8 @@ class ConsolePort(CableTermination, ComponentModel):
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.connected_endpoint.device.identifier if self.connected_endpoint else None,
|
||||
self.connected_endpoint.name if self.connected_endpoint else None,
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.get_connection_status_display(),
|
||||
)
|
||||
|
||||
|
||||
@ -1740,6 +1737,8 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
||||
objects = DeviceComponentManager()
|
||||
tags = TaggableManager()
|
||||
|
||||
csv_headers = ['device', 'name']
|
||||
|
||||
class Meta:
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
@ -1749,6 +1748,12 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
||||
def get_absolute_url(self):
|
||||
return self.device.get_absolute_url()
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Power ports
|
||||
@ -1781,7 +1786,7 @@ class PowerPort(CableTermination, ComponentModel):
|
||||
objects = DeviceComponentManager()
|
||||
tags = TaggableManager()
|
||||
|
||||
csv_headers = ['pdu', 'connected_endpoint', 'device', 'power_port', 'connection_status']
|
||||
csv_headers = ['device', 'name']
|
||||
|
||||
class Meta:
|
||||
ordering = ['device', 'name']
|
||||
@ -1795,11 +1800,8 @@ class PowerPort(CableTermination, ComponentModel):
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.connected_endpoint.device.identifier if self.connected_endpoint else None,
|
||||
self.connected_endpoint.name if self.connected_endpoint else None,
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.get_connection_status_display(),
|
||||
)
|
||||
|
||||
|
||||
@ -1823,6 +1825,8 @@ class PowerOutlet(CableTermination, ComponentModel):
|
||||
objects = DeviceComponentManager()
|
||||
tags = TaggableManager()
|
||||
|
||||
csv_headers = ['device', 'name']
|
||||
|
||||
class Meta:
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
@ -1832,6 +1836,12 @@ class PowerOutlet(CableTermination, ComponentModel):
|
||||
def get_absolute_url(self):
|
||||
return self.device.get_absolute_url()
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Interfaces
|
||||
@ -1935,6 +1945,11 @@ class Interface(CableTermination, ComponentModel):
|
||||
objects = InterfaceManager()
|
||||
tags = TaggableManager()
|
||||
|
||||
csv_headers = [
|
||||
'device', 'virtual_machine', 'name', 'lag', 'form_factor', 'enabled', 'mac_address', 'mtu', 'mgmt_only',
|
||||
'description', 'mode',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
ordering = ['device', 'name']
|
||||
unique_together = ['device', 'name']
|
||||
@ -1945,6 +1960,21 @@ class Interface(CableTermination, ComponentModel):
|
||||
def get_absolute_url(self):
|
||||
return reverse('dcim:interface', kwargs={'pk': self.pk})
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.device.identifier if self.device else None,
|
||||
self.virtual_machine.name if self.virtual_machine else None,
|
||||
self.name,
|
||||
self.lag.name if self.lag else None,
|
||||
self.get_form_factor_display(),
|
||||
self.enabled,
|
||||
self.mac_address,
|
||||
self.mtu,
|
||||
self.mgmt_only,
|
||||
self.description,
|
||||
self.get_mode_display(),
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
|
||||
# An Interface must belong to a Device *or* to a VirtualMachine
|
||||
@ -2112,6 +2142,8 @@ class FrontPort(CableTermination, ComponentModel):
|
||||
objects = DeviceComponentManager()
|
||||
tags = TaggableManager()
|
||||
|
||||
csv_headers = ['device', 'name', 'type', 'rear_port', 'rear_port_position']
|
||||
|
||||
class Meta:
|
||||
ordering = ['device', 'name']
|
||||
unique_together = [
|
||||
@ -2122,6 +2154,15 @@ class FrontPort(CableTermination, ComponentModel):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.get_type_display(),
|
||||
self.rear_port.name,
|
||||
self.rear_port_position,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate rear port assignment
|
||||
@ -2162,6 +2203,8 @@ class RearPort(CableTermination, ComponentModel):
|
||||
objects = DeviceComponentManager()
|
||||
tags = TaggableManager()
|
||||
|
||||
csv_headers = ['device', 'name', 'type', 'positions']
|
||||
|
||||
class Meta:
|
||||
ordering = ['device', 'name']
|
||||
unique_together = ['device', 'name']
|
||||
@ -2169,6 +2212,14 @@ class RearPort(CableTermination, ComponentModel):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.get_type_display(),
|
||||
self.positions,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Device bays
|
||||
@ -2198,6 +2249,8 @@ class DeviceBay(ComponentModel):
|
||||
objects = DeviceComponentManager()
|
||||
tags = TaggableManager()
|
||||
|
||||
csv_headers = ['device', 'name', 'installed_device']
|
||||
|
||||
class Meta:
|
||||
ordering = ['device', 'name']
|
||||
unique_together = ['device', 'name']
|
||||
@ -2208,6 +2261,13 @@ class DeviceBay(ComponentModel):
|
||||
def get_absolute_url(self):
|
||||
return self.device.get_absolute_url()
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.installed_device.identifier if self.installed_device else None,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate that the parent Device can have DeviceBays
|
||||
@ -2535,3 +2595,64 @@ class Cable(ChangeLoggedModel):
|
||||
return trace_cable(far_end, position)
|
||||
|
||||
return trace_cable(self.termination_a), trace_cable(self.termination_b)
|
||||
|
||||
|
||||
#
|
||||
# Connection proxy models
|
||||
#
|
||||
|
||||
class ConsoleConnection(ConsolePort):
|
||||
|
||||
csv_headers = [
|
||||
'console_server', 'port', 'device', 'console_port', 'connection_status',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.connected_endpoint.device.identifier if self.connected_endpoint else None,
|
||||
self.connected_endpoint.name if self.connected_endpoint else None,
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.get_connection_status_display(),
|
||||
)
|
||||
|
||||
|
||||
class PowerConnection(PowerPort):
|
||||
|
||||
csv_headers = [
|
||||
'pdu', 'outlet', 'device', 'power_port', 'connection_status',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.connected_endpoint.device.identifier if self.connected_endpoint else None,
|
||||
self.connected_endpoint.name if self.connected_endpoint else None,
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.get_connection_status_display(),
|
||||
)
|
||||
|
||||
|
||||
class InterfaceConnection(Interface):
|
||||
|
||||
csv_headers = [
|
||||
'device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.connected_endpoint.device.identifier if self.connected_endpoint else None,
|
||||
self.connected_endpoint.name if self.connected_endpoint else None,
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.get_connection_status_display(),
|
||||
)
|
||||
|
@ -4,10 +4,11 @@ from django_tables2.utils import Accessor
|
||||
from tenancy.tables import COL_TENANT
|
||||
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
InventoryItem, Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
Cable, ConsoleConnection, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device,
|
||||
DeviceBay, DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceConnection,
|
||||
InterfaceTemplate, InventoryItem, Manufacturer, Platform, PowerConnection, PowerOutlet, PowerOutletTemplate,
|
||||
PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
|
||||
REGION_LINK = """
|
||||
@ -668,19 +669,22 @@ class ConsoleConnectionTable(BaseTable):
|
||||
viewname='dcim:device',
|
||||
accessor=Accessor('connected_endpoint.device'),
|
||||
args=[Accessor('connected_endpoint.device.pk')],
|
||||
verbose_name='Console server'
|
||||
verbose_name='Console Server'
|
||||
)
|
||||
connected_endpoint = tables.Column(verbose_name='Port')
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
name = tables.Column(verbose_name='Console port')
|
||||
cable = tables.LinkColumn(
|
||||
viewname='dcim:cable',
|
||||
args=[Accessor('cable.pk')]
|
||||
connected_endpoint = tables.Column(
|
||||
verbose_name='Port'
|
||||
)
|
||||
device = tables.LinkColumn(
|
||||
viewname='dcim:device',
|
||||
args=[Accessor('device.pk')]
|
||||
)
|
||||
name = tables.Column(
|
||||
verbose_name='Console Port'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = ConsolePort
|
||||
fields = ('console_server', 'connected_endpoint', 'device', 'name', 'cable')
|
||||
model = ConsoleConnection
|
||||
fields = ('console_server', 'connected_endpoint', 'device', 'name', 'connection_status')
|
||||
|
||||
|
||||
class PowerConnectionTable(BaseTable):
|
||||
@ -690,17 +694,20 @@ class PowerConnectionTable(BaseTable):
|
||||
args=[Accessor('connected_endpoint.device.pk')],
|
||||
verbose_name='PDU'
|
||||
)
|
||||
connected_endpoint = tables.Column(verbose_name='Outlet')
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
name = tables.Column(verbose_name='Power Port')
|
||||
cable = tables.LinkColumn(
|
||||
viewname='dcim:cable',
|
||||
args=[Accessor('cable.pk')]
|
||||
connected_endpoint = tables.Column(
|
||||
verbose_name='Outlet'
|
||||
)
|
||||
device = tables.LinkColumn(
|
||||
viewname='dcim:device',
|
||||
args=[Accessor('device.pk')]
|
||||
)
|
||||
name = tables.Column(
|
||||
verbose_name='Power Port'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerPort
|
||||
fields = ('pdu', 'connected_endpoint', 'device', 'name', 'cable')
|
||||
model = PowerConnection
|
||||
fields = ('pdu', 'connected_endpoint', 'device', 'name', 'connection_status')
|
||||
|
||||
|
||||
class InterfaceConnectionTable(BaseTable):
|
||||
@ -736,14 +743,12 @@ class InterfaceConnectionTable(BaseTable):
|
||||
accessor=Accessor('connected_endpoint.description'),
|
||||
verbose_name='Description'
|
||||
)
|
||||
cable = tables.LinkColumn(
|
||||
viewname='dcim:cable',
|
||||
args=[Accessor('cable.pk')]
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Interface
|
||||
fields = ('device_a', 'interface_a', 'description_a', 'device_b', 'interface_b', 'description_b', 'cable')
|
||||
model = InterfaceConnection
|
||||
fields = (
|
||||
'device_a', 'interface_a', 'description_a', 'device_b', 'interface_b', 'description_b', 'connection_status',
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
|
@ -24,10 +24,11 @@ from utilities.views import (
|
||||
from virtualization.models import VirtualMachine
|
||||
from . import filters, forms, tables
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
Cable, ConsoleConnection, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device,
|
||||
DeviceBay, DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceConnection,
|
||||
InterfaceTemplate, InventoryItem, Manufacturer, Platform, PowerConnection, PowerOutlet, PowerOutletTemplate,
|
||||
PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
|
||||
|
||||
@ -1686,7 +1687,7 @@ class CableBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
#
|
||||
|
||||
class ConsoleConnectionsListView(ObjectListView):
|
||||
queryset = ConsolePort.objects.select_related(
|
||||
queryset = ConsoleConnection.objects.select_related(
|
||||
'device', 'connected_endpoint__device'
|
||||
).filter(
|
||||
connected_endpoint__isnull=False
|
||||
@ -1700,7 +1701,7 @@ class ConsoleConnectionsListView(ObjectListView):
|
||||
|
||||
|
||||
class PowerConnectionsListView(ObjectListView):
|
||||
queryset = PowerPort.objects.select_related(
|
||||
queryset = PowerConnection.objects.select_related(
|
||||
'device', 'connected_endpoint__device'
|
||||
).filter(
|
||||
connected_endpoint__isnull=False
|
||||
@ -1714,7 +1715,7 @@ class PowerConnectionsListView(ObjectListView):
|
||||
|
||||
|
||||
class InterfaceConnectionsListView(ObjectListView):
|
||||
queryset = Interface.objects.select_related(
|
||||
queryset = InterfaceConnection.objects.select_related(
|
||||
'device', 'cable', '_connected_interface__device'
|
||||
).filter(
|
||||
# Avoid duplicate connections by only selecting the lower PK in a connected pair
|
||||
|
Reference in New Issue
Block a user