Merge pull request #7297 from netbox-community/7295-tables-cleanup

Closes #7295: General cleanup of table classes
This commit is contained in:
Jeremy Stretch 2021-09-17 15:56:26 -04:00 committed by GitHub
commit 383cdb5340
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 431 additions and 422 deletions

View File

@ -6,6 +6,14 @@ from utilities.tables import BaseTable, ButtonsColumn, ChoiceFieldColumn, Markdo
from .models import * from .models import *
__all__ = (
'CircuitTable',
'CircuitTypeTable',
'ProviderTable',
'ProviderNetworkTable',
)
CIRCUITTERMINATION_LINK = """ CIRCUITTERMINATION_LINK = """
{% if value.site %} {% if value.site %}
<a href="{{ value.site.get_absolute_url }}">{{ value.site }}</a> <a href="{{ value.site.get_absolute_url }}">{{ value.site }}</a>

View File

@ -34,9 +34,7 @@ class ProviderView(generic.ObjectView):
).prefetch_related( ).prefetch_related(
'type', 'tenant', 'terminations__site' 'type', 'tenant', 'terminations__site'
) )
circuits_table = tables.CircuitTable(circuits, exclude=('provider',))
circuits_table = tables.CircuitTable(circuits)
circuits_table.columns.hide('provider')
paginate_table(circuits_table, request) paginate_table(circuits_table, request)
return { return {
@ -97,10 +95,7 @@ class ProviderNetworkView(generic.ObjectView):
).prefetch_related( ).prefetch_related(
'type', 'tenant', 'terminations__site' 'type', 'tenant', 'terminations__site'
) )
circuits_table = tables.CircuitTable(circuits) circuits_table = tables.CircuitTable(circuits)
circuits_table.columns.hide('termination_a')
circuits_table.columns.hide('termination_z')
paginate_table(circuits_table, request) paginate_table(circuits_table, request)
return { return {
@ -153,12 +148,8 @@ class CircuitTypeView(generic.ObjectView):
queryset = CircuitType.objects.all() queryset = CircuitType.objects.all()
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
circuits = Circuit.objects.restrict(request.user, 'view').filter( circuits = Circuit.objects.restrict(request.user, 'view').filter(type=instance)
type=instance circuits_table = tables.CircuitTable(circuits, exclude=('type',))
)
circuits_table = tables.CircuitTable(circuits)
circuits_table.columns.hide('type')
paginate_table(circuits_table, request) paginate_table(circuits_table, request)
return { return {

View File

@ -18,6 +18,7 @@ from .template_code import (
) )
__all__ = ( __all__ = (
'BaseInterfaceTable',
'ConsolePortTable', 'ConsolePortTable',
'ConsoleServerPortTable', 'ConsoleServerPortTable',
'DeviceBayTable', 'DeviceBayTable',

View File

@ -10,7 +10,6 @@ from utilities.tables import (
__all__ = ( __all__ = (
'RackTable', 'RackTable',
'RackDetailTable',
'RackReservationTable', 'RackReservationTable',
'RackRoleTable', 'RackRoleTable',
) )
@ -56,17 +55,6 @@ class RackTable(BaseTable):
template_code="{{ record.u_height }}U", template_code="{{ record.u_height }}U",
verbose_name='Height' verbose_name='Height'
) )
class Meta(BaseTable.Meta):
model = Rack
fields = (
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
'width', 'u_height',
)
default_columns = ('pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height')
class RackDetailTable(RackTable):
comments = MarkdownColumn() comments = MarkdownColumn()
device_count = LinkedCountColumn( device_count = LinkedCountColumn(
viewname='dcim:device_list', viewname='dcim:device_list',
@ -85,7 +73,8 @@ class RackDetailTable(RackTable):
url_name='dcim:rack_list' url_name='dcim:rack_list'
) )
class Meta(RackTable.Meta): class Meta(BaseTable.Meta):
model = Rack
fields = ( fields = (
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type', 'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
'width', 'u_height', 'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'tags', 'width', 'u_height', 'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'tags',

View File

@ -131,8 +131,7 @@ class RegionView(generic.ObjectView):
sites = Site.objects.restrict(request.user, 'view').filter( sites = Site.objects.restrict(request.user, 'view').filter(
region=instance region=instance
) )
sites_table = tables.SiteTable(sites) sites_table = tables.SiteTable(sites, exclude=('region',))
sites_table.columns.hide('region')
paginate_table(sites_table, request) paginate_table(sites_table, request)
return { return {
@ -216,8 +215,7 @@ class SiteGroupView(generic.ObjectView):
sites = Site.objects.restrict(request.user, 'view').filter( sites = Site.objects.restrict(request.user, 'view').filter(
group=instance group=instance
) )
sites_table = tables.SiteTable(sites) sites_table = tables.SiteTable(sites, exclude=('group',))
sites_table.columns.hide('group')
paginate_table(sites_table, request) paginate_table(sites_table, request)
return { return {
@ -453,8 +451,7 @@ class RackRoleView(generic.ObjectView):
role=instance role=instance
) )
racks_table = tables.RackTable(racks) racks_table = tables.RackTable(racks, exclude=('role', 'get_utilization', 'get_power_utilization'))
racks_table.columns.hide('role')
paginate_table(racks_table, request) paginate_table(racks_table, request)
return { return {
@ -505,7 +502,7 @@ class RackListView(generic.ObjectListView):
) )
filterset = filtersets.RackFilterSet filterset = filtersets.RackFilterSet
filterset_form = forms.RackFilterForm filterset_form = forms.RackFilterForm
table = tables.RackDetailTable table = tables.RackTable
class RackElevationListView(generic.ObjectListView): class RackElevationListView(generic.ObjectListView):
@ -704,8 +701,7 @@ class ManufacturerView(generic.ObjectView):
manufacturer=instance manufacturer=instance
) )
devicetypes_table = tables.DeviceTypeTable(devicetypes) devicetypes_table = tables.DeviceTypeTable(devicetypes, exclude=('manufacturer',))
devicetypes_table.columns.hide('manufacturer')
paginate_table(devicetypes_table, request) paginate_table(devicetypes_table, request)
return { return {
@ -1165,9 +1161,7 @@ class DeviceRoleView(generic.ObjectView):
devices = Device.objects.restrict(request.user, 'view').filter( devices = Device.objects.restrict(request.user, 'view').filter(
device_role=instance device_role=instance
) )
devices_table = tables.DeviceTable(devices, exclude=('device_role',))
devices_table = tables.DeviceTable(devices)
devices_table.columns.hide('device_role')
paginate_table(devices_table, request) paginate_table(devices_table, request)
return { return {
@ -1231,9 +1225,7 @@ class PlatformView(generic.ObjectView):
devices = Device.objects.restrict(request.user, 'view').filter( devices = Device.objects.restrict(request.user, 'view').filter(
platform=instance platform=instance
) )
devices_table = tables.DeviceTable(devices, exclude=('platform',))
devices_table = tables.DeviceTable(devices)
devices_table.columns.hide('platform')
paginate_table(devices_table, request) paginate_table(devices_table, request)
return { return {
@ -1878,9 +1870,9 @@ class InterfaceView(generic.ObjectView):
child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance) child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
child_interfaces_tables = tables.InterfaceTable( child_interfaces_tables = tables.InterfaceTable(
child_interfaces, child_interfaces,
exclude=('device', 'parent'),
orderable=False orderable=False
) )
child_interfaces_tables.columns.hide('device')
# Get assigned VLANs and annotate whether each is tagged or untagged # Get assigned VLANs and annotate whether each is tagged or untagged
vlans = [] vlans = []

View File

@ -7,6 +7,19 @@ from utilities.tables import (
) )
from .models import * from .models import *
__all__ = (
'ConfigContextTable',
'CustomFieldTable',
'CustomLinkTable',
'ExportTemplateTable',
'JournalEntryTable',
'ObjectChangeTable',
'ObjectJournalTable',
'TaggedItemTable',
'TagTable',
'WebhookTable',
)
CONFIGCONTEXT_ACTIONS = """ CONFIGCONTEXT_ACTIONS = """
{% if perms.extras.change_configcontext %} {% if perms.extras.change_configcontext %}
<a href="{% url 'extras:configcontext_edit' pk=record.pk %}" class="btn btn-sm btn-warning"><i class="mdi mdi-pencil" aria-hidden="true"></i></a> <a href="{% url 'extras:configcontext_edit' pk=record.pk %}" class="btn btn-sm btn-warning"><i class="mdi mdi-pencil" aria-hidden="true"></i></a>

View File

@ -0,0 +1,4 @@
from .ip import *
from .services import *
from .vlans import *
from .vrfs import *

View File

@ -2,14 +2,23 @@ import django_tables2 as tables
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django_tables2.utils import Accessor from django_tables2.utils import Accessor
from dcim.models import Interface
from tenancy.tables import TenantColumn from tenancy.tables import TenantColumn
from utilities.tables import ( from utilities.tables import (
BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ContentTypeColumn, LinkedCountColumn, TagColumn, BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, LinkedCountColumn, TagColumn,
ToggleColumn, UtilizationColumn, ToggleColumn, UtilizationColumn,
) )
from virtualization.models import VMInterface from ipam.models import *
from .models import *
__all__ = (
'AggregateTable',
'InterfaceIPAddressTable',
'IPAddressAssignTable',
'IPAddressTable',
'IPRangeTable',
'PrefixTable',
'RIRTable',
'RoleTable',
)
AVAILABLE_LABEL = mark_safe('<span class="badge bg-success">Available</span>') AVAILABLE_LABEL = mark_safe('<span class="badge bg-success">Available</span>')
@ -66,114 +75,6 @@ VRF_LINK = """
{% endif %} {% endif %}
""" """
VRF_TARGETS = """
{% for rt in value.all %}
<a href="{{ rt.get_absolute_url }}">{{ rt }}</a>{% if not forloop.last %}<br />{% endif %}
{% empty %}
&mdash;
{% endfor %}
"""
VLAN_LINK = """
{% if record.pk %}
<a href="{{ record.get_absolute_url }}">{{ record.vid }}</a>
{% elif perms.ipam.add_vlan %}
<a href="{% url 'ipam:vlan_add' %}?vid={{ record.vid }}{% if record.vlan_group %}&group={{ record.vlan_group.pk }}{% endif %}" class="btn btn-sm btn-success">{{ record.available }} VLAN{{ record.available|pluralize }} available</a>
{% else %}
{{ record.available }} VLAN{{ record.available|pluralize }} available
{% endif %}
"""
VLAN_PREFIXES = """
{% for prefix in record.prefixes.all %}
<a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a>{% if not forloop.last %}<br />{% endif %}
{% empty %}
&mdash;
{% endfor %}
"""
VLAN_ROLE_LINK = """
{% if record.role %}
<a href="{% url 'ipam:vlan_list' %}?role={{ record.role.slug }}">{{ record.role }}</a>
{% else %}
&mdash;
{% endif %}
"""
VLANGROUP_ADD_VLAN = """
{% with next_vid=record.get_next_available_vid %}
{% if next_vid and perms.ipam.add_vlan %}
<a href="{% url 'ipam:vlan_add' %}?group={{ record.pk }}&vid={{ next_vid }}" title="Add VLAN" class="btn btn-sm btn-success">
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
</a>
{% endif %}
{% endwith %}
"""
VLAN_MEMBER_TAGGED = """
{% if record.untagged_vlan_id == object.pk %}
<span class="text-danger"><i class="mdi mdi-close-thick"></i></span>
{% else %}
<span class="text-success"><i class="mdi mdi-check-bold"></i></span>
{% endif %}
"""
#
# VRFs
#
class VRFTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(
linkify=True
)
rd = tables.Column(
verbose_name='RD'
)
tenant = TenantColumn()
enforce_unique = BooleanColumn(
verbose_name='Unique'
)
import_targets = tables.TemplateColumn(
template_code=VRF_TARGETS,
orderable=False
)
export_targets = tables.TemplateColumn(
template_code=VRF_TARGETS,
orderable=False
)
tags = TagColumn(
url_name='ipam:vrf_list'
)
class Meta(BaseTable.Meta):
model = VRF
fields = (
'pk', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'tags',
)
default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
#
# Route targets
#
class RouteTargetTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(
linkify=True
)
tenant = TenantColumn()
tags = TagColumn(
url_name='ipam:vrf_list'
)
class Meta(BaseTable.Meta):
model = RouteTarget
fields = ('pk', 'name', 'tenant', 'description', 'tags')
default_columns = ('pk', 'name', 'tenant', 'description')
# #
# RIRs # RIRs
@ -215,13 +116,6 @@ class AggregateTable(BaseTable):
format="Y-m-d", format="Y-m-d",
verbose_name='Added' verbose_name='Added'
) )
class Meta(BaseTable.Meta):
model = Aggregate
fields = ('pk', 'prefix', 'rir', 'tenant', 'date_added', 'description')
class AggregateDetailTable(AggregateTable):
child_count = tables.Column( child_count = tables.Column(
verbose_name='Prefixes' verbose_name='Prefixes'
) )
@ -233,7 +127,8 @@ class AggregateDetailTable(AggregateTable):
url_name='ipam:aggregate_list' url_name='ipam:aggregate_list'
) )
class Meta(AggregateTable.Meta): class Meta(BaseTable.Meta):
model = Aggregate
fields = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description', 'tags') fields = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description', 'tags')
default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description') default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description')
@ -332,20 +227,6 @@ class PrefixTable(BaseTable):
mark_utilized = BooleanColumn( mark_utilized = BooleanColumn(
verbose_name='Marked Utilized' verbose_name='Marked Utilized'
) )
class Meta(BaseTable.Meta):
model = Prefix
fields = (
'pk', 'prefix', 'prefix_flat', 'status', 'depth', 'children', 'vrf', 'tenant', 'site', 'vlan', 'role',
'is_pool', 'mark_utilized', 'description',
)
default_columns = ('pk', 'prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'description')
row_attrs = {
'class': lambda record: 'success' if not record.pk else '',
}
class PrefixDetailTable(PrefixTable):
utilization = PrefixUtilizationColumn( utilization = PrefixUtilizationColumn(
accessor='get_utilization', accessor='get_utilization',
orderable=False orderable=False
@ -354,7 +235,8 @@ class PrefixDetailTable(PrefixTable):
url_name='ipam:prefix_list' url_name='ipam:prefix_list'
) )
class Meta(PrefixTable.Meta): class Meta(BaseTable.Meta):
model = Prefix
fields = ( fields = (
'pk', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'pk', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role',
'is_pool', 'mark_utilized', 'description', 'tags', 'is_pool', 'mark_utilized', 'description', 'tags',
@ -362,6 +244,9 @@ class PrefixDetailTable(PrefixTable):
default_columns = ( default_columns = (
'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description', 'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
) )
row_attrs = {
'class': lambda record: 'success' if not record.pk else '',
}
# #
@ -427,25 +312,11 @@ class IPAddressTable(BaseTable):
orderable=False, orderable=False,
verbose_name='Device/VM' verbose_name='Device/VM'
) )
class Meta(BaseTable.Meta):
model = IPAddress
fields = (
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned_object', 'assigned_object_parent', 'dns_name',
'description',
)
row_attrs = {
'class': lambda record: 'success' if not isinstance(record, IPAddress) else '',
}
class IPAddressDetailTable(IPAddressTable):
nat_inside = tables.Column( nat_inside = tables.Column(
linkify=True, linkify=True,
orderable=False, orderable=False,
verbose_name='NAT (Inside)' verbose_name='NAT (Inside)'
) )
tenant = TenantColumn()
assigned = BooleanColumn( assigned = BooleanColumn(
accessor='assigned_object_id', accessor='assigned_object_id',
verbose_name='Assigned' verbose_name='Assigned'
@ -454,14 +325,18 @@ class IPAddressDetailTable(IPAddressTable):
url_name='ipam:ipaddress_list' url_name='ipam:ipaddress_list'
) )
class Meta(IPAddressTable.Meta): class Meta(BaseTable.Meta):
model = IPAddress
fields = ( fields = (
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'assigned', 'dns_name', 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'assigned', 'dns_name', 'description',
'description', 'tags', 'tags',
) )
default_columns = ( default_columns = (
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned', 'dns_name', 'description', 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned', 'dns_name', 'description',
) )
row_attrs = {
'class': lambda record: 'success' if not isinstance(record, IPAddress) else '',
}
class IPAddressAssignTable(BaseTable): class IPAddressAssignTable(BaseTable):
@ -501,173 +376,3 @@ class InterfaceIPAddressTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = IPAddress model = IPAddress
fields = ('address', 'vrf', 'status', 'role', 'tenant', 'description') fields = ('address', 'vrf', 'status', 'role', 'tenant', 'description')
#
# VLAN groups
#
class VLANGroupTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(linkify=True)
scope_type = ContentTypeColumn()
scope = tables.Column(
linkify=True,
orderable=False
)
vlan_count = LinkedCountColumn(
viewname='ipam:vlan_list',
url_params={'group_id': 'pk'},
verbose_name='VLANs'
)
actions = ButtonsColumn(
model=VLANGroup,
prepend_template=VLANGROUP_ADD_VLAN
)
class Meta(BaseTable.Meta):
model = VLANGroup
fields = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'slug', 'description', 'actions')
default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'description', 'actions')
#
# VLANs
#
class VLANTable(BaseTable):
pk = ToggleColumn()
vid = tables.TemplateColumn(
template_code=VLAN_LINK,
verbose_name='ID'
)
site = tables.Column(
linkify=True
)
group = tables.Column(
linkify=True
)
tenant = TenantColumn()
status = ChoiceFieldColumn(
default=AVAILABLE_LABEL
)
role = tables.TemplateColumn(
template_code=VLAN_ROLE_LINK
)
class Meta(BaseTable.Meta):
model = VLAN
fields = ('pk', 'vid', 'name', 'site', 'group', 'tenant', 'status', 'role', 'description')
row_attrs = {
'class': lambda record: 'success' if not isinstance(record, VLAN) else '',
}
class VLANDetailTable(VLANTable):
prefixes = tables.TemplateColumn(
template_code=VLAN_PREFIXES,
orderable=False,
verbose_name='Prefixes'
)
tenant = TenantColumn()
tags = TagColumn(
url_name='ipam:vlan_list'
)
class Meta(VLANTable.Meta):
fields = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description', 'tags')
default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')
class VLANMembersTable(BaseTable):
"""
Base table for Interface and VMInterface assignments
"""
name = tables.Column(
linkify=True,
verbose_name='Interface'
)
tagged = tables.TemplateColumn(
template_code=VLAN_MEMBER_TAGGED,
orderable=False
)
class VLANDevicesTable(VLANMembersTable):
device = tables.Column(
linkify=True
)
actions = ButtonsColumn(Interface, buttons=['edit'])
class Meta(BaseTable.Meta):
model = Interface
fields = ('device', 'name', 'tagged', 'actions')
class VLANVirtualMachinesTable(VLANMembersTable):
virtual_machine = tables.Column(
linkify=True
)
actions = ButtonsColumn(VMInterface, buttons=['edit'])
class Meta(BaseTable.Meta):
model = VMInterface
fields = ('virtual_machine', 'name', 'tagged', 'actions')
class InterfaceVLANTable(BaseTable):
"""
List VLANs assigned to a specific Interface.
"""
vid = tables.Column(
linkify=True,
verbose_name='ID'
)
tagged = BooleanColumn()
site = tables.Column(
linkify=True
)
group = tables.Column(
accessor=Accessor('group__name'),
verbose_name='Group'
)
tenant = TenantColumn()
status = ChoiceFieldColumn()
role = tables.TemplateColumn(
template_code=VLAN_ROLE_LINK
)
class Meta(BaseTable.Meta):
model = VLAN
fields = ('vid', 'tagged', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
def __init__(self, interface, *args, **kwargs):
self.interface = interface
super().__init__(*args, **kwargs)
#
# Services
#
class ServiceTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(
linkify=True
)
parent = tables.Column(
linkify=True,
order_by=('device', 'virtual_machine')
)
ports = tables.TemplateColumn(
template_code='{{ record.port_list }}',
verbose_name='Ports'
)
tags = TagColumn(
url_name='ipam:service_list'
)
class Meta(BaseTable.Meta):
model = Service
fields = ('pk', 'name', 'parent', 'protocol', 'ports', 'ipaddresses', 'description', 'tags')
default_columns = ('pk', 'name', 'parent', 'protocol', 'ports', 'description')

View File

@ -0,0 +1,35 @@
import django_tables2 as tables
from utilities.tables import BaseTable, TagColumn, ToggleColumn
from ipam.models import *
__all__ = (
'ServiceTable',
)
#
# Services
#
class ServiceTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(
linkify=True
)
parent = tables.Column(
linkify=True,
order_by=('device', 'virtual_machine')
)
ports = tables.TemplateColumn(
template_code='{{ record.port_list }}',
verbose_name='Ports'
)
tags = TagColumn(
url_name='ipam:service_list'
)
class Meta(BaseTable.Meta):
model = Service
fields = ('pk', 'name', 'parent', 'protocol', 'ports', 'ipaddresses', 'description', 'tags')
default_columns = ('pk', 'name', 'parent', 'protocol', 'ports', 'description')

203
netbox/ipam/tables/vlans.py Normal file
View File

@ -0,0 +1,203 @@
import django_tables2 as tables
from django.utils.safestring import mark_safe
from django_tables2.utils import Accessor
from dcim.models import Interface
from tenancy.tables import TenantColumn
from utilities.tables import (
BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ContentTypeColumn, LinkedCountColumn, TagColumn,
ToggleColumn,
)
from virtualization.models import VMInterface
from ipam.models import *
__all__ = (
'InterfaceVLANTable',
'VLANDevicesTable',
'VLANGroupTable',
'VLANMembersTable',
'VLANTable',
'VLANVirtualMachinesTable',
)
AVAILABLE_LABEL = mark_safe('<span class="badge bg-success">Available</span>')
VLAN_LINK = """
{% if record.pk %}
<a href="{{ record.get_absolute_url }}">{{ record.vid }}</a>
{% elif perms.ipam.add_vlan %}
<a href="{% url 'ipam:vlan_add' %}?vid={{ record.vid }}{% if record.vlan_group %}&group={{ record.vlan_group.pk }}{% endif %}" class="btn btn-sm btn-success">{{ record.available }} VLAN{{ record.available|pluralize }} available</a>
{% else %}
{{ record.available }} VLAN{{ record.available|pluralize }} available
{% endif %}
"""
VLAN_PREFIXES = """
{% for prefix in record.prefixes.all %}
<a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a>{% if not forloop.last %}<br />{% endif %}
{% empty %}
&mdash;
{% endfor %}
"""
VLAN_ROLE_LINK = """
{% if record.role %}
<a href="{% url 'ipam:vlan_list' %}?role={{ record.role.slug }}">{{ record.role }}</a>
{% else %}
&mdash;
{% endif %}
"""
VLANGROUP_ADD_VLAN = """
{% with next_vid=record.get_next_available_vid %}
{% if next_vid and perms.ipam.add_vlan %}
<a href="{% url 'ipam:vlan_add' %}?group={{ record.pk }}&vid={{ next_vid }}" title="Add VLAN" class="btn btn-sm btn-success">
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
</a>
{% endif %}
{% endwith %}
"""
VLAN_MEMBER_TAGGED = """
{% if record.untagged_vlan_id == object.pk %}
<span class="text-danger"><i class="mdi mdi-close-thick"></i></span>
{% else %}
<span class="text-success"><i class="mdi mdi-check-bold"></i></span>
{% endif %}
"""
#
# VLAN groups
#
class VLANGroupTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(linkify=True)
scope_type = ContentTypeColumn()
scope = tables.Column(
linkify=True,
orderable=False
)
vlan_count = LinkedCountColumn(
viewname='ipam:vlan_list',
url_params={'group_id': 'pk'},
verbose_name='VLANs'
)
actions = ButtonsColumn(
model=VLANGroup,
prepend_template=VLANGROUP_ADD_VLAN
)
class Meta(BaseTable.Meta):
model = VLANGroup
fields = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'slug', 'description', 'actions')
default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'description', 'actions')
#
# VLANs
#
class VLANTable(BaseTable):
pk = ToggleColumn()
vid = tables.TemplateColumn(
template_code=VLAN_LINK,
verbose_name='ID'
)
site = tables.Column(
linkify=True
)
group = tables.Column(
linkify=True
)
tenant = TenantColumn()
status = ChoiceFieldColumn(
default=AVAILABLE_LABEL
)
role = tables.TemplateColumn(
template_code=VLAN_ROLE_LINK
)
prefixes = tables.TemplateColumn(
template_code=VLAN_PREFIXES,
orderable=False,
verbose_name='Prefixes'
)
tags = TagColumn(
url_name='ipam:vlan_list'
)
class Meta(BaseTable.Meta):
model = VLAN
fields = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description', 'tags')
default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')
row_attrs = {
'class': lambda record: 'success' if not isinstance(record, VLAN) else '',
}
class VLANMembersTable(BaseTable):
"""
Base table for Interface and VMInterface assignments
"""
name = tables.Column(
linkify=True,
verbose_name='Interface'
)
tagged = tables.TemplateColumn(
template_code=VLAN_MEMBER_TAGGED,
orderable=False
)
class VLANDevicesTable(VLANMembersTable):
device = tables.Column(
linkify=True
)
actions = ButtonsColumn(Interface, buttons=['edit'])
class Meta(BaseTable.Meta):
model = Interface
fields = ('device', 'name', 'tagged', 'actions')
class VLANVirtualMachinesTable(VLANMembersTable):
virtual_machine = tables.Column(
linkify=True
)
actions = ButtonsColumn(VMInterface, buttons=['edit'])
class Meta(BaseTable.Meta):
model = VMInterface
fields = ('virtual_machine', 'name', 'tagged', 'actions')
class InterfaceVLANTable(BaseTable):
"""
List VLANs assigned to a specific Interface.
"""
vid = tables.Column(
linkify=True,
verbose_name='ID'
)
tagged = BooleanColumn()
site = tables.Column(
linkify=True
)
group = tables.Column(
accessor=Accessor('group__name'),
verbose_name='Group'
)
tenant = TenantColumn()
status = ChoiceFieldColumn()
role = tables.TemplateColumn(
template_code=VLAN_ROLE_LINK
)
class Meta(BaseTable.Meta):
model = VLAN
fields = ('vid', 'tagged', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
def __init__(self, interface, *args, **kwargs):
self.interface = interface
super().__init__(*args, **kwargs)

View File

@ -0,0 +1,74 @@
import django_tables2 as tables
from tenancy.tables import TenantColumn
from utilities.tables import BaseTable, BooleanColumn, TagColumn, ToggleColumn
from ipam.models import *
__all__ = (
'RouteTargetTable',
'VRFTable',
)
VRF_TARGETS = """
{% for rt in value.all %}
<a href="{{ rt.get_absolute_url }}">{{ rt }}</a>{% if not forloop.last %}<br />{% endif %}
{% empty %}
&mdash;
{% endfor %}
"""
#
# VRFs
#
class VRFTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(
linkify=True
)
rd = tables.Column(
verbose_name='RD'
)
tenant = TenantColumn()
enforce_unique = BooleanColumn(
verbose_name='Unique'
)
import_targets = tables.TemplateColumn(
template_code=VRF_TARGETS,
orderable=False
)
export_targets = tables.TemplateColumn(
template_code=VRF_TARGETS,
orderable=False
)
tags = TagColumn(
url_name='ipam:vrf_list'
)
class Meta(BaseTable.Meta):
model = VRF
fields = (
'pk', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'tags',
)
default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
#
# Route targets
#
class RouteTargetTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(
linkify=True
)
tenant = TenantColumn()
tags = TagColumn(
url_name='ipam:vrf_list'
)
class Meta(BaseTable.Meta):
model = RouteTarget
fields = ('pk', 'name', 'tenant', 'description', 'tags')
default_columns = ('pk', 'name', 'tenant', 'description')

View File

@ -155,9 +155,7 @@ class RIRView(generic.ObjectView):
aggregates = Aggregate.objects.restrict(request.user, 'view').filter( aggregates = Aggregate.objects.restrict(request.user, 'view').filter(
rir=instance rir=instance
) )
aggregates_table = tables.AggregateTable(aggregates, exclude=('rir', 'utilization'))
aggregates_table = tables.AggregateTable(aggregates)
aggregates_table.columns.hide('rir')
paginate_table(aggregates_table, request) paginate_table(aggregates_table, request)
return { return {
@ -207,7 +205,7 @@ class AggregateListView(generic.ObjectListView):
) )
filterset = filtersets.AggregateFilterSet filterset = filtersets.AggregateFilterSet
filterset_form = forms.AggregateFilterForm filterset_form = forms.AggregateFilterForm
table = tables.AggregateDetailTable table = tables.AggregateTable
class AggregateView(generic.ObjectView): class AggregateView(generic.ObjectView):
@ -227,7 +225,7 @@ class AggregateView(generic.ObjectView):
if request.GET.get('show_available', 'true') == 'true': if request.GET.get('show_available', 'true') == 'true':
child_prefixes = add_available_prefixes(instance.prefix, child_prefixes) child_prefixes = add_available_prefixes(instance.prefix, child_prefixes)
prefix_table = tables.PrefixDetailTable(child_prefixes) prefix_table = tables.PrefixTable(child_prefixes, exclude=('utilization',))
if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'): if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
prefix_table.columns.show('pk') prefix_table.columns.show('pk')
paginate_table(prefix_table, request) paginate_table(prefix_table, request)
@ -296,8 +294,7 @@ class RoleView(generic.ObjectView):
role=instance role=instance
) )
prefixes_table = tables.PrefixTable(prefixes) prefixes_table = tables.PrefixTable(prefixes, exclude=('role', 'utilization'))
prefixes_table.columns.hide('role')
paginate_table(prefixes_table, request) paginate_table(prefixes_table, request)
return { return {
@ -340,7 +337,7 @@ class PrefixListView(generic.ObjectListView):
queryset = Prefix.objects.all() queryset = Prefix.objects.all()
filterset = filtersets.PrefixFilterSet filterset = filtersets.PrefixFilterSet
filterset_form = forms.PrefixFilterForm filterset_form = forms.PrefixFilterForm
table = tables.PrefixDetailTable table = tables.PrefixTable
template_name = 'ipam/prefix_list.html' template_name = 'ipam/prefix_list.html'
@ -363,8 +360,11 @@ class PrefixView(generic.ObjectView):
).prefetch_related( ).prefetch_related(
'site', 'role' 'site', 'role'
) )
parent_prefix_table = tables.PrefixTable(list(parent_prefixes), orderable=False) parent_prefix_table = tables.PrefixTable(
parent_prefix_table.exclude = ('vrf',) list(parent_prefixes),
exclude=('vrf', 'utilization'),
orderable=False
)
# Duplicate prefixes table # Duplicate prefixes table
duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter( duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
@ -374,8 +374,11 @@ class PrefixView(generic.ObjectView):
).prefetch_related( ).prefetch_related(
'site', 'role' 'site', 'role'
) )
duplicate_prefix_table = tables.PrefixTable(list(duplicate_prefixes), orderable=False) duplicate_prefix_table = tables.PrefixTable(
duplicate_prefix_table.exclude = ('vrf',) list(duplicate_prefixes),
exclude=('vrf', 'utilization'),
orderable=False
)
return { return {
'aggregate': aggregate, 'aggregate': aggregate,
@ -398,7 +401,7 @@ class PrefixPrefixesView(generic.ObjectView):
if child_prefixes and request.GET.get('show_available', 'true') == 'true': if child_prefixes and request.GET.get('show_available', 'true') == 'true':
child_prefixes = add_available_prefixes(instance.prefix, child_prefixes) child_prefixes = add_available_prefixes(instance.prefix, child_prefixes)
table = tables.PrefixDetailTable(child_prefixes, user=request.user) table = tables.PrefixTable(child_prefixes, user=request.user, exclude=('utilization',))
if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'): if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
table.columns.show('pk') table.columns.show('pk')
paginate_table(table, request) paginate_table(table, request)
@ -601,7 +604,7 @@ class IPAddressListView(generic.ObjectListView):
queryset = IPAddress.objects.all() queryset = IPAddress.objects.all()
filterset = filtersets.IPAddressFilterSet filterset = filtersets.IPAddressFilterSet
filterset_form = forms.IPAddressFilterForm filterset_form = forms.IPAddressFilterForm
table = tables.IPAddressDetailTable table = tables.IPAddressTable
class IPAddressView(generic.ObjectView): class IPAddressView(generic.ObjectView):
@ -615,8 +618,11 @@ class IPAddressView(generic.ObjectView):
).prefetch_related( ).prefetch_related(
'site', 'role' 'site', 'role'
) )
parent_prefixes_table = tables.PrefixTable(list(parent_prefixes), orderable=False) parent_prefixes_table = tables.PrefixTable(
parent_prefixes_table.exclude = ('vrf',) list(parent_prefixes),
exclude=('vrf', 'utilization'),
orderable=False
)
# Duplicate IPs table # Duplicate IPs table
duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter( duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
@ -767,11 +773,9 @@ class VLANGroupView(generic.ObjectView):
vlans_count = vlans.count() vlans_count = vlans.count()
vlans = add_available_vlans(vlans, vlan_group=instance) vlans = add_available_vlans(vlans, vlan_group=instance)
vlans_table = tables.VLANDetailTable(vlans) vlans_table = tables.VLANTable(vlans, exclude=('site', 'group', 'prefixes'))
if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'): if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
vlans_table.columns.show('pk') vlans_table.columns.show('pk')
vlans_table.columns.hide('site')
vlans_table.columns.hide('group')
paginate_table(vlans_table, request) paginate_table(vlans_table, request)
# Compile permissions list for rendering the object table # Compile permissions list for rendering the object table
@ -828,7 +832,7 @@ class VLANListView(generic.ObjectListView):
queryset = VLAN.objects.all() queryset = VLAN.objects.all()
filterset = filtersets.VLANFilterSet filterset = filtersets.VLANFilterSet
filterset_form = forms.VLANFilterForm filterset_form = forms.VLANFilterForm
table = tables.VLANDetailTable table = tables.VLANTable
class VLANView(generic.ObjectView): class VLANView(generic.ObjectView):
@ -838,8 +842,7 @@ class VLANView(generic.ObjectView):
prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=instance).prefetch_related( prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=instance).prefetch_related(
'vrf', 'site', 'role' 'vrf', 'site', 'role'
) )
prefix_table = tables.PrefixTable(list(prefixes), orderable=False) prefix_table = tables.PrefixTable(list(prefixes), exclude=('vlan', 'utilization'), orderable=False)
prefix_table.exclude = ('vlan',)
return { return {
'prefix_table': prefix_table, 'prefix_table': prefix_table,

View File

@ -21,7 +21,7 @@ from tenancy.tables import TenantTable
from utilities.utils import count_related from utilities.utils import count_related
from virtualization.filtersets import ClusterFilterSet, VirtualMachineFilterSet from virtualization.filtersets import ClusterFilterSet, VirtualMachineFilterSet
from virtualization.models import Cluster, VirtualMachine from virtualization.models import Cluster, VirtualMachine
from virtualization.tables import ClusterTable, VirtualMachineDetailTable from virtualization.tables import ClusterTable, VirtualMachineTable
SEARCH_MAX_RESULTS = 15 SEARCH_MAX_RESULTS = 15
SEARCH_TYPES = OrderedDict(( SEARCH_TYPES = OrderedDict((
@ -130,7 +130,7 @@ SEARCH_TYPES = OrderedDict((
'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6',
), ),
'filterset': VirtualMachineFilterSet, 'filterset': VirtualMachineFilterSet,
'table': VirtualMachineDetailTable, 'table': VirtualMachineTable,
'url': 'virtualization:virtualmachine_list', 'url': 'virtualization:virtualmachine_list',
}), }),
# IPAM # IPAM

View File

@ -15,9 +15,9 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12">
{% include 'inc/table_controls.html' with table_modal="PrefixDetailTable_config" %} {% include 'inc/table_controls.html' with table_modal="PrefixTable_config" %}
{% include 'utilities/obj_table.html' with heading='Child Prefixes' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' parent=prefix %} {% include 'utilities/obj_table.html' with heading='Child Prefixes' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' parent=prefix %}
</div> </div>
</div> </div>
{% table_config_form table table_name="PrefixDetailTable" %} {% table_config_form table table_name="PrefixTable" %}
{% endblock %} {% endblock %}

View File

@ -5,6 +5,12 @@ from utilities.tables import (
) )
from .models import Tenant, TenantGroup from .models import Tenant, TenantGroup
__all__ = (
'TenantColumn',
'TenantGroupTable',
'TenantTable',
)
# #
# Table columns # Table columns

View File

@ -32,9 +32,7 @@ class TenantGroupView(generic.ObjectView):
tenants = Tenant.objects.restrict(request.user, 'view').filter( tenants = Tenant.objects.restrict(request.user, 'view').filter(
group=instance group=instance
) )
tenants_table = tables.TenantTable(tenants, exclude=('group',))
tenants_table = tables.TenantTable(tenants)
tenants_table.columns.hide('group')
paginate_table(tenants_table, request) paginate_table(tenants_table, request)
return { return {

View File

@ -12,12 +12,13 @@ __all__ = (
'ClusterTable', 'ClusterTable',
'ClusterGroupTable', 'ClusterGroupTable',
'ClusterTypeTable', 'ClusterTypeTable',
'VirtualMachineDetailTable',
'VirtualMachineTable', 'VirtualMachineTable',
'VirtualMachineVMInterfaceTable', 'VirtualMachineVMInterfaceTable',
'VMInterfaceTable', 'VMInterfaceTable',
) )
PRIMARY_IP_ORDERING = ('primary_ip4', 'primary_ip6') if settings.PREFER_IPV4 else ('primary_ip6', 'primary_ip4')
VMINTERFACE_BUTTONS = """ VMINTERFACE_BUTTONS = """
{% if perms.ipam.add_ipaddress %} {% if perms.ipam.add_ipaddress %}
<a href="{% url 'ipam:ipaddress_add' %}?vminterface={{ record.pk }}&return_url={{ virtualmachine.get_absolute_url }}" class="btn btn-sm btn-success" title="Add IP Address"> <a href="{% url 'ipam:ipaddress_add' %}?vminterface={{ record.pk }}&return_url={{ virtualmachine.get_absolute_url }}" class="btn btn-sm btn-success" title="Add IP Address">
@ -118,13 +119,7 @@ class VirtualMachineTable(BaseTable):
) )
role = ColoredLabelColumn() role = ColoredLabelColumn()
tenant = TenantColumn() tenant = TenantColumn()
comments = MarkdownColumn()
class Meta(BaseTable.Meta):
model = VirtualMachine
fields = ('pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk')
class VirtualMachineDetailTable(VirtualMachineTable):
primary_ip4 = tables.Column( primary_ip4 = tables.Column(
linkify=True, linkify=True,
verbose_name='IPv4 Address' verbose_name='IPv4 Address'
@ -133,19 +128,11 @@ class VirtualMachineDetailTable(VirtualMachineTable):
linkify=True, linkify=True,
verbose_name='IPv6 Address' verbose_name='IPv6 Address'
) )
if settings.PREFER_IPV4: primary_ip = tables.Column(
primary_ip = tables.Column( linkify=True,
linkify=True, order_by=PRIMARY_IP_ORDERING,
order_by=('primary_ip4', 'primary_ip6'), verbose_name='IP Address'
verbose_name='IP Address' )
)
else:
primary_ip = tables.Column(
linkify=True,
order_by=('primary_ip6', 'primary_ip4'),
verbose_name='IP Address'
)
comments = MarkdownColumn()
tags = TagColumn( tags = TagColumn(
url_name='virtualization:virtualmachine_list' url_name='virtualization:virtualmachine_list'
) )

View File

@ -39,9 +39,7 @@ class ClusterTypeView(generic.ObjectView):
device_count=count_related(Device, 'cluster'), device_count=count_related(Device, 'cluster'),
vm_count=count_related(VirtualMachine, 'cluster') vm_count=count_related(VirtualMachine, 'cluster')
) )
clusters_table = tables.ClusterTable(clusters, exclude=('type',))
clusters_table = tables.ClusterTable(clusters)
clusters_table.columns.hide('type')
paginate_table(clusters_table, request) paginate_table(clusters_table, request)
return { return {
@ -103,9 +101,7 @@ class ClusterGroupView(generic.ObjectView):
device_count=count_related(Device, 'cluster'), device_count=count_related(Device, 'cluster'),
vm_count=count_related(VirtualMachine, 'cluster') vm_count=count_related(VirtualMachine, 'cluster')
) )
clusters_table = tables.ClusterTable(clusters, exclude=('group',))
clusters_table = tables.ClusterTable(clusters)
clusters_table.columns.hide('group')
paginate_table(clusters_table, request) paginate_table(clusters_table, request)
return { return {
@ -171,7 +167,11 @@ class ClusterVirtualMachinesView(generic.ObjectView):
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
virtualmachines = VirtualMachine.objects.restrict(request.user, 'view').filter(cluster=instance) virtualmachines = VirtualMachine.objects.restrict(request.user, 'view').filter(cluster=instance)
virtualmachines_table = tables.VirtualMachineTable(virtualmachines, orderable=False) virtualmachines_table = tables.VirtualMachineTable(
virtualmachines,
exclude=('cluster',),
orderable=False
)
return { return {
'virtualmachines_table': virtualmachines_table, 'virtualmachines_table': virtualmachines_table,
@ -315,7 +315,7 @@ class VirtualMachineListView(generic.ObjectListView):
queryset = VirtualMachine.objects.all() queryset = VirtualMachine.objects.all()
filterset = filtersets.VirtualMachineFilterSet filterset = filtersets.VirtualMachineFilterSet
filterset_form = forms.VirtualMachineFilterForm filterset_form = forms.VirtualMachineFilterForm
table = tables.VirtualMachineDetailTable table = tables.VirtualMachineTable
template_name = 'virtualization/virtualmachine_list.html' template_name = 'virtualization/virtualmachine_list.html'
@ -430,9 +430,9 @@ class VMInterfaceView(generic.ObjectView):
child_interfaces = VMInterface.objects.restrict(request.user, 'view').filter(parent=instance) child_interfaces = VMInterface.objects.restrict(request.user, 'view').filter(parent=instance)
child_interfaces_tables = tables.VMInterfaceTable( child_interfaces_tables = tables.VMInterfaceTable(
child_interfaces, child_interfaces,
exclude=('virtual_machine',),
orderable=False orderable=False
) )
child_interfaces_tables.columns.hide('virtual_machine')
# Get assigned VLANs and annotate whether each is tagged or untagged # Get assigned VLANs and annotate whether each is tagged or untagged
vlans = [] vlans = []