Merge branch 'develop' into feature

This commit is contained in:
jeremystretch 2022-07-11 12:58:24 -04:00
commit c380fd00bf
29 changed files with 153 additions and 140 deletions

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v3.2.5 placeholder: v3.2.6
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v3.2.5 placeholder: v3.2.6
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -1,3 +1,7 @@
# HTML sanitizer
# https://github.com/mozilla/bleach
bleach
# The Python web framework on which NetBox is built # The Python web framework on which NetBox is built
# https://github.com/django/django # https://github.com/django/django
Django Django
@ -130,7 +134,3 @@ tablib
# Timezone data (required by django-timezone-field on Python 3.9+) # Timezone data (required by django-timezone-field on Python 3.9+)
# https://github.com/python/tzdata # https://github.com/python/tzdata
tzdata tzdata
# HTML sanitizer
# https://github.com/mozilla/bleach
bleach

View File

@ -10,7 +10,7 @@ Within the database, custom fields are stored as JSON data directly alongside ea
Custom fields may be created by navigating to Customization > Custom Fields. NetBox supports six types of custom field: Custom fields may be created by navigating to Customization > Custom Fields. NetBox supports six types of custom field:
* Text: Free-form text (up to 255 characters) * Text: Free-form text (intended for single-line use)
* Long text: Free-form of any length; supports Markdown rendering * Long text: Free-form of any length; supports Markdown rendering
* Integer: A whole number (positive or negative) * Integer: A whole number (positive or negative)
* Boolean: True or false * Boolean: True or false

View File

@ -1,6 +1,10 @@
# NetBox v3.2 # NetBox v3.2
## v3.2.6 (FUTURE) ## v3.2.7 (FUTURE)
---
## v3.2.6 (2022-07-11)
### Enhancements ### Enhancements
@ -8,14 +12,18 @@
* [#9396](https://github.com/netbox-community/netbox/issues/9396) - Allow filtering modules by bay ID * [#9396](https://github.com/netbox-community/netbox/issues/9396) - Allow filtering modules by bay ID
* [#9403](https://github.com/netbox-community/netbox/issues/9403) - Enable modifying virtual chassis properties when creating/editing a device * [#9403](https://github.com/netbox-community/netbox/issues/9403) - Enable modifying virtual chassis properties when creating/editing a device
* [#9540](https://github.com/netbox-community/netbox/issues/9540) - Add filters for assigned device & VM to IP addresses list * [#9540](https://github.com/netbox-community/netbox/issues/9540) - Add filters for assigned device & VM to IP addresses list
* [#9686](https://github.com/netbox-community/netbox/issues/9686) - Add tenant group column for all object tables with tenant assignments
### Bug Fixes ### Bug Fixes
* [#8854](https://github.com/netbox-community/netbox/issues/8854) - Fix `REMOTE_AUTH_DEFAULT_GROUPS` for social-auth backends * [#8854](https://github.com/netbox-community/netbox/issues/8854) - Fix `REMOTE_AUTH_DEFAULT_GROUPS` for social-auth backends
* [#9575](https://github.com/netbox-community/netbox/issues/9575) - Fix AttributeError exception for FHRP group with an IP address assigned * [#9575](https://github.com/netbox-community/netbox/issues/9575) - Fix AttributeError exception for FHRP group with an IP address assigned
* [#9597](https://github.com/netbox-community/netbox/issues/9597) - Include `installed_module` in module bay REST API serializer * [#9597](https://github.com/netbox-community/netbox/issues/9597) - Include `installed_module` in module bay REST API serializer
* [#9632](https://github.com/netbox-community/netbox/issues/9632) - Automatically focus on search box when expanding dropdowns
* [#9657](https://github.com/netbox-community/netbox/issues/9657) - Fix filtering for custom fields and webhooks in the UI * [#9657](https://github.com/netbox-community/netbox/issues/9657) - Fix filtering for custom fields and webhooks in the UI
* [#9682](https://github.com/netbox-community/netbox/issues/9682) - Fix bulk assignment of ASNs to sites * [#9682](https://github.com/netbox-community/netbox/issues/9682) - Fix bulk assignment of ASNs to sites
* [#9687](https://github.com/netbox-community/netbox/issues/9687) - Don't restrict custom text field lengths when entering via UI form
* [#9704](https://github.com/netbox-community/netbox/issues/9704) - Include `last_updated` field on JournalEntry REST API serializer
--- ---

View File

@ -2,7 +2,7 @@ import django_tables2 as tables
from circuits.models import * from circuits.models import *
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin
from .columns import CommitRateColumn from .columns import CommitRateColumn
__all__ = ( __all__ = (
@ -39,7 +39,7 @@ class CircuitTypeTable(NetBoxTable):
default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug') default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug')
class CircuitTable(NetBoxTable): class CircuitTable(TenancyColumnsMixin, NetBoxTable):
cid = tables.Column( cid = tables.Column(
linkify=True, linkify=True,
verbose_name='Circuit ID' verbose_name='Circuit ID'
@ -48,7 +48,6 @@ class CircuitTable(NetBoxTable):
linkify=True linkify=True
) )
status = columns.ChoiceFieldColumn() status = columns.ChoiceFieldColumn()
tenant = TenantColumn()
termination_a = tables.TemplateColumn( termination_a = tables.TemplateColumn(
template_code=CIRCUITTERMINATION_LINK, template_code=CIRCUITTERMINATION_LINK,
verbose_name='Side A' verbose_name='Side A'
@ -69,8 +68,9 @@ class CircuitTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Circuit model = Circuit
fields = ( fields = (
'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'install_date', 'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'tenant_group', 'termination_a', 'termination_z',
'termination_date', 'commit_rate', 'description', 'comments', 'contacts', 'tags', 'created', 'last_updated', 'install_date', 'termination_date', 'commit_rate', 'description', 'comments', 'contacts', 'tags', 'created',
'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description', 'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description',

View File

@ -30,7 +30,7 @@ class ProviderView(generic.ObjectView):
circuits = Circuit.objects.restrict(request.user, 'view').filter( circuits = Circuit.objects.restrict(request.user, 'view').filter(
provider=instance provider=instance
).prefetch_related( ).prefetch_related(
'type', 'tenant', 'terminations__site' 'type', 'tenant', 'tenant__group', 'terminations__site'
) )
circuits_table = tables.CircuitTable(circuits, user=request.user, exclude=('provider',)) circuits_table = tables.CircuitTable(circuits, user=request.user, exclude=('provider',))
circuits_table.configure(request) circuits_table.configure(request)
@ -91,7 +91,7 @@ class ProviderNetworkView(generic.ObjectView):
Q(termination_a__provider_network=instance.pk) | Q(termination_a__provider_network=instance.pk) |
Q(termination_z__provider_network=instance.pk) Q(termination_z__provider_network=instance.pk)
).prefetch_related( ).prefetch_related(
'type', 'tenant', 'terminations__site' 'type', 'tenant', 'tenant__group', 'terminations__site'
) )
circuits_table = tables.CircuitTable(circuits, user=request.user) circuits_table = tables.CircuitTable(circuits, user=request.user)
circuits_table.configure(request) circuits_table.configure(request)
@ -192,7 +192,7 @@ class CircuitTypeBulkDeleteView(generic.BulkDeleteView):
class CircuitListView(generic.ObjectListView): class CircuitListView(generic.ObjectListView):
queryset = Circuit.objects.prefetch_related( queryset = Circuit.objects.prefetch_related(
'provider', 'type', 'tenant', 'termination_a', 'termination_z' 'provider', 'type', 'tenant', 'tenant__group', 'termination_a', 'termination_z'
) )
filterset = filtersets.CircuitFilterSet filterset = filtersets.CircuitFilterSet
filterset_form = forms.CircuitFilterForm filterset_form = forms.CircuitFilterForm

View File

@ -96,8 +96,7 @@ class ModularComponentModel(ComponentModel):
inventory_items = GenericRelation( inventory_items = GenericRelation(
to='dcim.InventoryItem', to='dcim.InventoryItem',
content_type_field='component_type', content_type_field='component_type',
object_id_field='component_id', object_id_field='component_id'
related_name='%(class)ss',
) )
class Meta: class Meta:

View File

@ -4,7 +4,7 @@ from django.utils.safestring import mark_safe
from dcim.models import Cable from dcim.models import Cable
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin
from .template_code import CABLE_LENGTH from .template_code import CABLE_LENGTH
__all__ = ( __all__ = (
@ -46,7 +46,7 @@ class CableTerminationsColumn(tables.Column):
# Cables # Cables
# #
class CableTable(NetBoxTable): class CableTable(TenancyColumnsMixin, NetBoxTable):
a_terminations = CableTerminationsColumn( a_terminations = CableTerminationsColumn(
cable_end='A', cable_end='A',
orderable=False, orderable=False,
@ -106,7 +106,6 @@ class CableTable(NetBoxTable):
verbose_name='Site B' verbose_name='Site B'
) )
status = columns.ChoiceFieldColumn() status = columns.ChoiceFieldColumn()
tenant = TenantColumn()
length = columns.TemplateColumn( length = columns.TemplateColumn(
template_code=CABLE_LENGTH, template_code=CABLE_LENGTH,
order_by=('_abs_length', 'length_unit') order_by=('_abs_length', 'length_unit')
@ -120,8 +119,8 @@ class CableTable(NetBoxTable):
model = Cable model = Cable
fields = ( fields = (
'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'device_a', 'device_b', 'rack_a', 'rack_b', 'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'device_a', 'device_b', 'rack_a', 'rack_b',
'location_a', 'location_b', 'site_a', 'site_b', 'status', 'type', 'tenant', 'color', 'length', 'tags', 'location_a', 'location_b', 'site_a', 'site_b', 'status', 'type', 'tenant', 'tenant_group', 'color',
'created', 'last_updated', 'length', 'tags', 'created', 'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'status', 'type', 'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'status', 'type',

View File

@ -6,7 +6,7 @@ from dcim.models import (
InventoryItemRole, ModuleBay, Platform, PowerOutlet, PowerPort, RearPort, VirtualChassis, InventoryItemRole, ModuleBay, Platform, PowerOutlet, PowerPort, RearPort, VirtualChassis,
) )
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin
from .template_code import * from .template_code import *
__all__ = ( __all__ = (
@ -137,13 +137,12 @@ class PlatformTable(NetBoxTable):
# Devices # Devices
# #
class DeviceTable(NetBoxTable): class DeviceTable(TenancyColumnsMixin, NetBoxTable):
name = tables.TemplateColumn( name = tables.TemplateColumn(
order_by=('_name',), order_by=('_name',),
template_code=DEVICE_LINK template_code=DEVICE_LINK
) )
status = columns.ChoiceFieldColumn() status = columns.ChoiceFieldColumn()
tenant = TenantColumn()
site = tables.Column( site = tables.Column(
linkify=True linkify=True
) )
@ -200,7 +199,7 @@ class DeviceTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Device model = Device
fields = ( fields = (
'pk', 'id', 'name', 'status', 'tenant', 'device_role', 'manufacturer', 'device_type', 'platform', 'serial', 'pk', 'id', 'name', 'status', 'tenant', 'tenant_group', 'device_role', 'manufacturer', 'device_type', 'platform', 'serial',
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'primary_ip', 'airflow', 'primary_ip4', 'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'primary_ip', 'airflow', 'primary_ip4',
'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'contacts', 'tags', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'contacts', 'tags',
'created', 'last_updated', 'created', 'last_updated',
@ -211,12 +210,11 @@ class DeviceTable(NetBoxTable):
) )
class DeviceImportTable(NetBoxTable): class DeviceImportTable(TenancyColumnsMixin, NetBoxTable):
name = tables.TemplateColumn( name = tables.TemplateColumn(
template_code=DEVICE_LINK template_code=DEVICE_LINK
) )
status = columns.ChoiceFieldColumn() status = columns.ChoiceFieldColumn()
tenant = TenantColumn()
site = tables.Column( site = tables.Column(
linkify=True linkify=True
) )
@ -232,7 +230,7 @@ class DeviceImportTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Device model = Device
fields = ('id', 'name', 'status', 'tenant', 'site', 'rack', 'position', 'device_role', 'device_type') fields = ('id', 'name', 'status', 'tenant', 'tenant_group', 'site', 'rack', 'position', 'device_role', 'device_type')
empty_text = False empty_text = False

View File

@ -3,7 +3,7 @@ from django_tables2.utils import Accessor
from dcim.models import Rack, RackReservation, RackRole from dcim.models import Rack, RackReservation, RackRole
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin
__all__ = ( __all__ = (
'RackTable', 'RackTable',
@ -37,7 +37,7 @@ class RackRoleTable(NetBoxTable):
# Racks # Racks
# #
class RackTable(NetBoxTable): class RackTable(TenancyColumnsMixin, NetBoxTable):
name = tables.Column( name = tables.Column(
order_by=('_name',), order_by=('_name',),
linkify=True linkify=True
@ -48,7 +48,6 @@ class RackTable(NetBoxTable):
site = tables.Column( site = tables.Column(
linkify=True linkify=True
) )
tenant = TenantColumn()
status = columns.ChoiceFieldColumn() status = columns.ChoiceFieldColumn()
role = columns.ColoredLabelColumn() role = columns.ColoredLabelColumn()
u_height = tables.TemplateColumn( u_height = tables.TemplateColumn(
@ -87,7 +86,7 @@ class RackTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Rack model = Rack
fields = ( fields = (
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', 'serial', 'asset_tag',
'type', 'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization', 'type', 'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization',
'get_power_utilization', 'contacts', 'tags', 'created', 'last_updated', 'get_power_utilization', 'contacts', 'tags', 'created', 'last_updated',
) )
@ -101,7 +100,7 @@ class RackTable(NetBoxTable):
# Rack reservations # Rack reservations
# #
class RackReservationTable(NetBoxTable): class RackReservationTable(TenancyColumnsMixin, NetBoxTable):
reservation = tables.Column( reservation = tables.Column(
accessor='pk', accessor='pk',
linkify=True linkify=True
@ -110,7 +109,6 @@ class RackReservationTable(NetBoxTable):
accessor=Accessor('rack__site'), accessor=Accessor('rack__site'),
linkify=True linkify=True
) )
tenant = TenantColumn()
rack = tables.Column( rack = tables.Column(
linkify=True linkify=True
) )
@ -125,7 +123,7 @@ class RackReservationTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = RackReservation model = RackReservation
fields = ( fields = (
'pk', 'id', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'tags', 'pk', 'id', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'tenant_group', 'description', 'tags',
'actions', 'created', 'last_updated', 'actions', 'created', 'last_updated',
) )
default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description') default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description')

View File

@ -2,7 +2,7 @@ import django_tables2 as tables
from dcim.models import Location, Region, Site, SiteGroup from dcim.models import Location, Region, Site, SiteGroup
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin
from .template_code import LOCATION_BUTTONS from .template_code import LOCATION_BUTTONS
__all__ = ( __all__ = (
@ -75,7 +75,7 @@ class SiteGroupTable(NetBoxTable):
# Sites # Sites
# #
class SiteTable(NetBoxTable): class SiteTable(TenancyColumnsMixin, NetBoxTable):
name = tables.Column( name = tables.Column(
linkify=True linkify=True
) )
@ -96,7 +96,6 @@ class SiteTable(NetBoxTable):
url_params={'site_id': 'pk'}, url_params={'site_id': 'pk'},
verbose_name='ASN Count' verbose_name='ASN Count'
) )
tenant = TenantColumn()
comments = columns.MarkdownColumn() comments = columns.MarkdownColumn()
contacts = columns.ManyToManyColumn( contacts = columns.ManyToManyColumn(
linkify_item=True linkify_item=True
@ -108,7 +107,7 @@ class SiteTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Site model = Site
fields = ( fields = (
'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'asns', 'asn_count', 'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'tenant_group', 'asns', 'asn_count',
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments',
'contacts', 'tags', 'created', 'last_updated', 'actions', 'contacts', 'tags', 'created', 'last_updated', 'actions',
) )
@ -119,15 +118,13 @@ class SiteTable(NetBoxTable):
# Locations # Locations
# #
class LocationTable(NetBoxTable): class LocationTable(TenancyColumnsMixin, NetBoxTable):
name = columns.MPTTColumn( name = columns.MPTTColumn(
linkify=True linkify=True
) )
site = tables.Column( site = tables.Column(
linkify=True linkify=True
) )
status = columns.ChoiceFieldColumn()
tenant = TenantColumn()
rack_count = columns.LinkedCountColumn( rack_count = columns.LinkedCountColumn(
viewname='dcim:rack_list', viewname='dcim:rack_list',
url_params={'location_id': 'pk'}, url_params={'location_id': 'pk'},
@ -151,7 +148,7 @@ class LocationTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Location model = Location
fields = ( fields = (
'pk', 'id', 'name', 'site', 'status', 'tenant', 'rack_count', 'device_count', 'description', 'slug', 'pk', 'id', 'name', 'site', 'status', 'tenant', 'tenant_group', 'rack_count', 'device_count', 'description',
'contacts', 'tags', 'actions', 'created', 'last_updated', 'slug', 'contacts', 'tags', 'actions', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'site', 'status', 'tenant', 'rack_count', 'device_count', 'description') default_columns = ('pk', 'name', 'site', 'status', 'tenant', 'rack_count', 'device_count', 'description')

View File

@ -572,9 +572,7 @@ class RackRoleBulkDeleteView(generic.BulkDeleteView):
# #
class RackListView(generic.ObjectListView): class RackListView(generic.ObjectListView):
queryset = Rack.objects.prefetch_related( queryset = Rack.objects.prefetch_related('devices__device_type').annotate(
'site', 'location', 'tenant', 'role', 'devices__device_type'
).annotate(
device_count=count_related(Device, 'rack') device_count=count_related(Device, 'rack')
) )
filterset = filtersets.RackFilterSet filterset = filtersets.RackFilterSet

View File

@ -222,7 +222,7 @@ class JournalEntrySerializer(NetBoxModelSerializer):
model = JournalEntry model = JournalEntry
fields = [ fields = [
'id', 'url', 'display', 'assigned_object_type', 'assigned_object_id', 'assigned_object', 'created', 'id', 'url', 'display', 'assigned_object_type', 'assigned_object_id', 'assigned_object', 'created',
'created_by', 'kind', 'comments', 'tags', 'custom_fields', 'created_by', 'kind', 'comments', 'tags', 'custom_fields', 'last_updated',
] ]
def validate(self, data): def validate(self, data):

View File

@ -377,13 +377,8 @@ class CustomField(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel):
# Text # Text
else: else:
if self.type == CustomFieldTypeChoices.TYPE_LONGTEXT: widget = forms.Textarea if self.type == CustomFieldTypeChoices.TYPE_LONGTEXT else None
max_length = None field = forms.CharField(required=required, initial=initial, widget=widget)
widget = forms.Textarea
else:
max_length = 255
widget = None
field = forms.CharField(max_length=max_length, required=required, initial=initial, widget=widget)
if self.validation_regex: if self.validation_regex:
field.validators = [ field.validators = [
RegexValidator( RegexValidator(

View File

@ -4,7 +4,7 @@ from django_tables2.utils import Accessor
from ipam.models import * from ipam.models import *
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin, TenantColumn
__all__ = ( __all__ = (
'AggregateTable', 'AggregateTable',
@ -99,7 +99,7 @@ class RIRTable(NetBoxTable):
# ASNs # ASNs
# #
class ASNTable(NetBoxTable): class ASNTable(TenancyColumnsMixin, NetBoxTable):
asn = tables.Column( asn = tables.Column(
linkify=True linkify=True
) )
@ -122,7 +122,6 @@ class ASNTable(NetBoxTable):
linkify_item=True, linkify_item=True,
verbose_name='Sites' verbose_name='Sites'
) )
tenant = TenantColumn()
tags = columns.TagColumn( tags = columns.TagColumn(
url_name='ipam:asn_list' url_name='ipam:asn_list'
) )
@ -130,7 +129,7 @@ class ASNTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = ASN model = ASN
fields = ( fields = (
'pk', 'asn', 'asn_asdot', 'rir', 'site_count', 'provider_count', 'tenant', 'description', 'sites', 'tags', 'pk', 'asn', 'asn_asdot', 'rir', 'site_count', 'provider_count', 'tenant', 'tenant_group', 'description', 'sites', 'tags',
'created', 'last_updated', 'actions', 'created', 'last_updated', 'actions',
) )
default_columns = ('pk', 'asn', 'rir', 'site_count', 'provider_count', 'sites', 'description', 'tenant') default_columns = ('pk', 'asn', 'rir', 'site_count', 'provider_count', 'sites', 'description', 'tenant')
@ -140,12 +139,11 @@ class ASNTable(NetBoxTable):
# Aggregates # Aggregates
# #
class AggregateTable(NetBoxTable): class AggregateTable(TenancyColumnsMixin, NetBoxTable):
prefix = tables.Column( prefix = tables.Column(
linkify=True, linkify=True,
verbose_name='Aggregate' verbose_name='Aggregate'
) )
tenant = TenantColumn()
date_added = tables.DateColumn( date_added = tables.DateColumn(
format="Y-m-d", format="Y-m-d",
verbose_name='Added' verbose_name='Added'
@ -164,7 +162,7 @@ class AggregateTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Aggregate model = Aggregate
fields = ( fields = (
'pk', 'id', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description', 'tags', 'pk', 'id', 'prefix', 'rir', 'tenant', 'tenant_group', 'child_count', 'utilization', 'date_added', 'description', 'tags',
'created', 'last_updated', 'created', 'last_updated',
) )
default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description') default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description')
@ -225,7 +223,7 @@ class PrefixUtilizationColumn(columns.UtilizationColumn):
""" """
class PrefixTable(NetBoxTable): class PrefixTable(TenancyColumnsMixin, NetBoxTable):
prefix = columns.TemplateColumn( prefix = columns.TemplateColumn(
template_code=PREFIX_LINK, template_code=PREFIX_LINK,
export_raw=True, export_raw=True,
@ -256,7 +254,6 @@ class PrefixTable(NetBoxTable):
template_code=VRF_LINK, template_code=VRF_LINK,
verbose_name='VRF' verbose_name='VRF'
) )
tenant = TenantColumn()
site = tables.Column( site = tables.Column(
linkify=True linkify=True
) )
@ -289,7 +286,7 @@ class PrefixTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Prefix model = Prefix
fields = ( fields = (
'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'tenant_group', 'site',
'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'tags', 'created', 'last_updated', 'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'tags', 'created', 'last_updated',
) )
default_columns = ( default_columns = (
@ -303,7 +300,7 @@ class PrefixTable(NetBoxTable):
# #
# IP ranges # IP ranges
# #
class IPRangeTable(NetBoxTable): class IPRangeTable(TenancyColumnsMixin, NetBoxTable):
start_address = tables.Column( start_address = tables.Column(
linkify=True linkify=True
) )
@ -317,7 +314,6 @@ class IPRangeTable(NetBoxTable):
role = tables.Column( role = tables.Column(
linkify=True linkify=True
) )
tenant = TenantColumn()
utilization = columns.UtilizationColumn( utilization = columns.UtilizationColumn(
accessor='utilization', accessor='utilization',
orderable=False orderable=False
@ -329,7 +325,7 @@ class IPRangeTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = IPRange model = IPRange
fields = ( fields = (
'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'description', 'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'description',
'utilization', 'tags', 'created', 'last_updated', 'utilization', 'tags', 'created', 'last_updated',
) )
default_columns = ( default_columns = (
@ -344,7 +340,7 @@ class IPRangeTable(NetBoxTable):
# IPAddresses # IPAddresses
# #
class IPAddressTable(NetBoxTable): class IPAddressTable(TenancyColumnsMixin, NetBoxTable):
address = tables.TemplateColumn( address = tables.TemplateColumn(
template_code=IPADDRESS_LINK, template_code=IPADDRESS_LINK,
verbose_name='IP Address' verbose_name='IP Address'
@ -357,7 +353,6 @@ class IPAddressTable(NetBoxTable):
default=AVAILABLE_LABEL default=AVAILABLE_LABEL
) )
role = columns.ChoiceFieldColumn() role = columns.ChoiceFieldColumn()
tenant = TenantColumn()
assigned_object = tables.Column( assigned_object = tables.Column(
linkify=True, linkify=True,
orderable=False, orderable=False,
@ -386,7 +381,7 @@ class IPAddressTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = IPAddress model = IPAddress
fields = ( fields = (
'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'assigned', 'dns_name', 'description', 'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'nat_inside', 'assigned', 'dns_name', 'description',
'tags', 'created', 'last_updated', 'tags', 'created', 'last_updated',
) )
default_columns = ( default_columns = (

View File

@ -5,7 +5,7 @@ from django_tables2.utils import Accessor
from dcim.models import Interface from dcim.models import Interface
from ipam.models import * from ipam.models import *
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin, TenantColumn
from virtualization.models import VMInterface from virtualization.models import VMInterface
__all__ = ( __all__ = (
@ -90,7 +90,7 @@ class VLANGroupTable(NetBoxTable):
# VLANs # VLANs
# #
class VLANTable(NetBoxTable): class VLANTable(TenancyColumnsMixin, NetBoxTable):
vid = tables.TemplateColumn( vid = tables.TemplateColumn(
template_code=VLAN_LINK, template_code=VLAN_LINK,
verbose_name='VID' verbose_name='VID'
@ -104,7 +104,6 @@ class VLANTable(NetBoxTable):
group = tables.Column( group = tables.Column(
linkify=True linkify=True
) )
tenant = TenantColumn()
status = columns.ChoiceFieldColumn( status = columns.ChoiceFieldColumn(
default=AVAILABLE_LABEL default=AVAILABLE_LABEL
) )
@ -123,7 +122,7 @@ class VLANTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = VLAN model = VLAN
fields = ( fields = (
'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description', 'tags', 'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'tenant_group', 'status', 'role', 'description', 'tags',
'created', 'last_updated', 'created', 'last_updated',
) )
default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description') default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')

View File

@ -2,7 +2,7 @@ import django_tables2 as tables
from ipam.models import * from ipam.models import *
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin
__all__ = ( __all__ = (
'RouteTargetTable', 'RouteTargetTable',
@ -20,14 +20,13 @@ VRF_TARGETS = """
# VRFs # VRFs
# #
class VRFTable(NetBoxTable): class VRFTable(TenancyColumnsMixin, NetBoxTable):
name = tables.Column( name = tables.Column(
linkify=True linkify=True
) )
rd = tables.Column( rd = tables.Column(
verbose_name='RD' verbose_name='RD'
) )
tenant = TenantColumn()
enforce_unique = columns.BooleanColumn( enforce_unique = columns.BooleanColumn(
verbose_name='Unique' verbose_name='Unique'
) )
@ -46,7 +45,7 @@ class VRFTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = VRF model = VRF
fields = ( fields = (
'pk', 'id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'pk', 'id', 'name', 'rd', 'tenant', 'tenant_group', 'enforce_unique', 'description', 'import_targets', 'export_targets',
'tags', 'created', 'last_updated', 'tags', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'rd', 'tenant', 'description') default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
@ -56,16 +55,15 @@ class VRFTable(NetBoxTable):
# Route targets # Route targets
# #
class RouteTargetTable(NetBoxTable): class RouteTargetTable(TenancyColumnsMixin, NetBoxTable):
name = tables.Column( name = tables.Column(
linkify=True linkify=True
) )
tenant = TenantColumn()
tags = columns.TagColumn( tags = columns.TagColumn(
url_name='ipam:vrf_list' url_name='ipam:vrf_list'
) )
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = RouteTarget model = RouteTarget
fields = ('pk', 'id', 'name', 'tenant', 'description', 'tags', 'created', 'last_updated',) fields = ('pk', 'id', 'name', 'tenant', 'tenant_group', 'description', 'tags', 'created', 'last_updated',)
default_columns = ('pk', 'name', 'tenant', 'description') default_columns = ('pk', 'name', 'tenant', 'description')

View File

@ -299,7 +299,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView):
def get_children(self, request, parent): def get_children(self, request, parent):
return Prefix.objects.restrict(request.user, 'view').filter( return Prefix.objects.restrict(request.user, 'view').filter(
prefix__net_contained_or_equal=str(parent.prefix) prefix__net_contained_or_equal=str(parent.prefix)
).prefetch_related('site', 'role', 'tenant', 'vlan') ).prefetch_related('site', 'role', 'tenant', 'tenant__group', 'vlan')
def prep_table_data(self, request, queryset, parent): def prep_table_data(self, request, queryset, parent):
# Determine whether to show assigned prefixes, available prefixes, or both # Determine whether to show assigned prefixes, available prefixes, or both
@ -471,7 +471,7 @@ class PrefixPrefixesView(generic.ObjectChildrenView):
def get_children(self, request, parent): def get_children(self, request, parent):
return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related( return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
'site', 'vrf', 'vlan', 'role', 'tenant', 'site', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group'
) )
def prep_table_data(self, request, queryset, parent): def prep_table_data(self, request, queryset, parent):
@ -500,7 +500,7 @@ class PrefixIPRangesView(generic.ObjectChildrenView):
def get_children(self, request, parent): def get_children(self, request, parent):
return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related( return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related(
'vrf', 'role', 'tenant', 'vrf', 'role', 'tenant', 'tenant__group',
) )
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
@ -587,9 +587,7 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView):
template_name = 'ipam/iprange/ip_addresses.html' template_name = 'ipam/iprange/ip_addresses.html'
def get_children(self, request, parent): def get_children(self, request, parent):
return parent.get_child_ips().restrict(request.user, 'view').prefetch_related( return parent.get_child_ips().restrict(request.user, 'view')
'vrf', 'tenant',
)
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
return { return {

View File

@ -34,7 +34,7 @@ CIRCUIT_TYPES = OrderedDict(
}), }),
('circuit', { ('circuit', {
'queryset': Circuit.objects.prefetch_related( 'queryset': Circuit.objects.prefetch_related(
'type', 'provider', 'tenant', 'terminations__site' 'type', 'provider', 'tenant', 'tenant__group', 'terminations__site'
), ),
'filterset': circuits.filtersets.CircuitFilterSet, 'filterset': circuits.filtersets.CircuitFilterSet,
'table': circuits.tables.CircuitTable, 'table': circuits.tables.CircuitTable,
@ -53,13 +53,13 @@ CIRCUIT_TYPES = OrderedDict(
DCIM_TYPES = OrderedDict( DCIM_TYPES = OrderedDict(
( (
('site', { ('site', {
'queryset': Site.objects.prefetch_related('region', 'tenant'), 'queryset': Site.objects.prefetch_related('region', 'tenant', 'tenant__group'),
'filterset': dcim.filtersets.SiteFilterSet, 'filterset': dcim.filtersets.SiteFilterSet,
'table': dcim.tables.SiteTable, 'table': dcim.tables.SiteTable,
'url': 'dcim:site_list', 'url': 'dcim:site_list',
}), }),
('rack', { ('rack', {
'queryset': Rack.objects.prefetch_related('site', 'location', 'tenant', 'role').annotate( 'queryset': Rack.objects.prefetch_related('site', 'location', 'tenant', 'tenant__group', 'role').annotate(
device_count=count_related(Device, 'rack') device_count=count_related(Device, 'rack')
), ),
'filterset': dcim.filtersets.RackFilterSet, 'filterset': dcim.filtersets.RackFilterSet,
@ -100,7 +100,7 @@ DCIM_TYPES = OrderedDict(
}), }),
('device', { ('device', {
'queryset': Device.objects.prefetch_related( 'queryset': Device.objects.prefetch_related(
'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6', 'device_type__manufacturer', 'device_role', 'tenant', 'tenant__group', 'site', 'rack', 'primary_ip4', 'primary_ip6',
), ),
'filterset': dcim.filtersets.DeviceFilterSet, 'filterset': dcim.filtersets.DeviceFilterSet,
'table': dcim.tables.DeviceTable, 'table': dcim.tables.DeviceTable,
@ -148,7 +148,7 @@ DCIM_TYPES = OrderedDict(
IPAM_TYPES = OrderedDict( IPAM_TYPES = OrderedDict(
( (
('vrf', { ('vrf', {
'queryset': VRF.objects.prefetch_related('tenant'), 'queryset': VRF.objects.prefetch_related('tenant', 'tenant__group'),
'filterset': ipam.filtersets.VRFFilterSet, 'filterset': ipam.filtersets.VRFFilterSet,
'table': ipam.tables.VRFTable, 'table': ipam.tables.VRFTable,
'url': 'ipam:vrf_list', 'url': 'ipam:vrf_list',
@ -160,25 +160,25 @@ IPAM_TYPES = OrderedDict(
'url': 'ipam:aggregate_list', 'url': 'ipam:aggregate_list',
}), }),
('prefix', { ('prefix', {
'queryset': Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'), 'queryset': Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'tenant__group', 'vlan', 'role'),
'filterset': ipam.filtersets.PrefixFilterSet, 'filterset': ipam.filtersets.PrefixFilterSet,
'table': ipam.tables.PrefixTable, 'table': ipam.tables.PrefixTable,
'url': 'ipam:prefix_list', 'url': 'ipam:prefix_list',
}), }),
('ipaddress', { ('ipaddress', {
'queryset': IPAddress.objects.prefetch_related('vrf__tenant', 'tenant'), 'queryset': IPAddress.objects.prefetch_related('vrf__tenant', 'tenant', 'tenant__group'),
'filterset': ipam.filtersets.IPAddressFilterSet, 'filterset': ipam.filtersets.IPAddressFilterSet,
'table': ipam.tables.IPAddressTable, 'table': ipam.tables.IPAddressTable,
'url': 'ipam:ipaddress_list', 'url': 'ipam:ipaddress_list',
}), }),
('vlan', { ('vlan', {
'queryset': VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role'), 'queryset': VLAN.objects.prefetch_related('site', 'group', 'tenant', 'tenant__group', 'role'),
'filterset': ipam.filtersets.VLANFilterSet, 'filterset': ipam.filtersets.VLANFilterSet,
'table': ipam.tables.VLANTable, 'table': ipam.tables.VLANTable,
'url': 'ipam:vlan_list', 'url': 'ipam:vlan_list',
}), }),
('asn', { ('asn', {
'queryset': ASN.objects.prefetch_related('rir', 'tenant'), 'queryset': ASN.objects.prefetch_related('rir', 'tenant', 'tenant__group'),
'filterset': ipam.filtersets.ASNFilterSet, 'filterset': ipam.filtersets.ASNFilterSet,
'table': ipam.tables.ASNTable, 'table': ipam.tables.ASNTable,
'url': 'ipam:asn_list', 'url': 'ipam:asn_list',
@ -223,7 +223,7 @@ VIRTUALIZATION_TYPES = OrderedDict(
}), }),
('virtualmachine', { ('virtualmachine', {
'queryset': VirtualMachine.objects.prefetch_related( 'queryset': VirtualMachine.objects.prefetch_related(
'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'cluster', 'tenant', 'tenant__group', 'platform', 'primary_ip4', 'primary_ip6',
), ),
'filterset': virtualization.filtersets.VirtualMachineFilterSet, 'filterset': virtualization.filtersets.VirtualMachineFilterSet,
'table': virtualization.tables.VirtualMachineTable, 'table': virtualization.tables.VirtualMachineTable,

Binary file not shown.

Binary file not shown.

View File

@ -411,6 +411,7 @@ export class APISelect {
} finally { } finally {
this.setOptionStyles(); this.setOptionStyles();
this.enable(); this.enable();
this.slim.slim.search.input.focus();
this.base.dispatchEvent(this.loadEvent); this.base.dispatchEvent(this.loadEvent);
} }
} }

View File

@ -48,9 +48,9 @@
<th scope="row">Rack</th> <th scope="row">Rack</th>
<td> <td>
{% if object.rack %} {% if object.rack %}
<a href="{% url 'dcim:rack' pk=object.rack.pk %}">{{ object.rack }}</a> {{ object.rack|linkify }}
<div class="float-end noprint"> <div class="float-end noprint">
<a href="{% url 'dcim:rack' pk=object.rack.pk %}?device={{ object.pk }}" class="btn btn-primary btn-sm" title="Highlight device"> <a href="{{ object.rack.get_absolute_url }}?device={{ object.pk }}" class="btn btn-primary btn-sm" title="Highlight device">
<i class="mdi mdi-view-day-outline"></i> <i class="mdi mdi-view-day-outline"></i>
</a> </a>
</div> </div>
@ -166,9 +166,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Role</th> <th scope="row">Role</th>
<td> <td>{{ object.device_role|linkify }}</td>
<a href="{% url 'dcim:device_list' %}?role={{ object.device_role.slug }}">{{ object.device_role }}</a>
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Platform</th> <th scope="row">Platform</th>
@ -178,7 +176,7 @@
<th scope="row">Primary IPv4</th> <th scope="row">Primary IPv4</th>
<td> <td>
{% if object.primary_ip4 %} {% if object.primary_ip4 %}
<a href="{% url 'ipam:ipaddress' pk=object.primary_ip4.pk %}">{{ object.primary_ip4.address.ip }}</a> <a href="{{ object.primary_ip4.get_absolute_url }}">{{ object.primary_ip4.address.ip }}</a>
{% if object.primary_ip4.nat_inside %} {% if object.primary_ip4.nat_inside %}
(NAT for {{ object.primary_ip4.nat_inside.address.ip|linkify }}) (NAT for {{ object.primary_ip4.nat_inside.address.ip|linkify }})
{% elif object.primary_ip4.nat_outside %} {% elif object.primary_ip4.nat_outside %}
@ -193,7 +191,7 @@
<th scope="row">Primary IPv6</th> <th scope="row">Primary IPv6</th>
<td> <td>
{% if object.primary_ip6 %} {% if object.primary_ip6 %}
<a href="{% url 'ipam:ipaddress' pk=object.primary_ip6.pk %}">{{ object.primary_ip6.address.ip }}</a> <a href="{{ object.primary_ip6.get_absolute_url }}">{{ object.primary_ip6.address.ip }}</a>
{% if object.primary_ip6.nat_inside %} {% if object.primary_ip6.nat_inside %}
(NAT for {{ object.primary_ip6.nat_inside.address.ip|linkify }}) (NAT for {{ object.primary_ip6.nat_inside.address.ip|linkify }})
{% elif object.primary_ip6.nat_outside %} {% elif object.primary_ip6.nat_outside %}

View File

@ -5,25 +5,29 @@
{% render_errors form %} {% render_errors form %}
{% block content %} {% block content %}
{% if perms.extras.add_journalentry %}
<form action="{% url 'extras:journalentry_add' %}" method="post" enctype="multipart/form-data">
<div class="container">
<div class="field-group">
<h4>New Journal Entry</h4>
{% csrf_token %}
{% render_form form %}
</div>
<div class="col col-md-12 text-end my-3">
<a href="{{ object.get_absolute_url }}" class="btn btn-outline-danger">Cancel</a>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</form>
{% endif %}
<div class="card"> <div class="card">
<div class="card-body table-responsive"> <div class="card-body table-responsive">
{% render_table table 'inc/table.html' %} {% render_table table 'inc/table.html' %}
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
</div> </div>
</div> </div>
{% if perms.extras.add_journalentry %}
<div class="card">
<div class="card-body table-responsive">
<h4 class="card-header">New Journal Entry</h4>
<form action="{% url 'extras:journalentry_add' %}" method="post" enctype="multipart/form-data">
<div class="container">
<div class="field-group">
{% csrf_token %}
{% render_form form %}
</div>
<div class="col col-md-12 text-end my-3">
<a href="{{ object.get_absolute_url }}" class="btn btn-outline-danger">Cancel</a>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</form>
</div>
</div>
{% endif %}
{% endblock %} {% endblock %}

View File

@ -2,6 +2,8 @@ import django_tables2 as tables
__all__ = ( __all__ = (
'TenantColumn', 'TenantColumn',
'TenantGroupColumn',
'TenancyColumnsMixin',
) )
@ -24,3 +26,32 @@ class TenantColumn(tables.TemplateColumn):
def value(self, value): def value(self, value):
return str(value) if value else None return str(value) if value else None
class TenantGroupColumn(tables.TemplateColumn):
"""
Include the tenant group description.
"""
template_code = """
{% if record.tenant and record.tenant.group %}
<a href="{{ record.tenant.group.get_absolute_url }}" title="{{ record.tenant.group.description }}">{{ record.tenant.group }}</a>
{% elif record.vrf.tenant and record.vrf.tenant.group %}
<a href="{{ record.vrf.tenant.group.get_absolute_url }}" title="{{ record.vrf.tenant.group.description }}">{{ record.vrf.tenant.group }}</a>*
{% else %}
&mdash;
{% endif %}
"""
def __init__(self, accessor=tables.A('tenant__group'), *args, **kwargs):
if 'verbose_name' not in kwargs:
kwargs['verbose_name'] = 'Tenant Group'
super().__init__(template_code=self.template_code, accessor=accessor, *args, **kwargs)
def value(self, value):
return str(value) if value else None
class TenancyColumnsMixin(tables.Table):
tenant_group = TenantGroupColumn()
tenant = TenantColumn()

View File

@ -1,6 +1,7 @@
import django_tables2 as tables import django_tables2 as tables
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenancyColumnsMixin
from virtualization.models import Cluster, ClusterGroup, ClusterType from virtualization.models import Cluster, ClusterGroup, ClusterType
__all__ = ( __all__ = (
@ -56,7 +57,7 @@ class ClusterGroupTable(NetBoxTable):
default_columns = ('pk', 'name', 'cluster_count', 'description') default_columns = ('pk', 'name', 'cluster_count', 'description')
class ClusterTable(NetBoxTable): class ClusterTable(TenancyColumnsMixin, NetBoxTable):
name = tables.Column( name = tables.Column(
linkify=True linkify=True
) )
@ -66,10 +67,6 @@ class ClusterTable(NetBoxTable):
group = tables.Column( group = tables.Column(
linkify=True linkify=True
) )
status = columns.ChoiceFieldColumn()
tenant = tables.Column(
linkify=True
)
site = tables.Column( site = tables.Column(
linkify=True linkify=True
) )
@ -94,7 +91,7 @@ class ClusterTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Cluster model = Cluster
fields = ( fields = (
'pk', 'id', 'name', 'type', 'group', 'status', 'tenant', 'site', 'comments', 'device_count', 'vm_count', 'pk', 'id', 'name', 'type', 'group', 'status', 'tenant', 'tenant_group', 'site', 'comments', 'device_count',
'contacts', 'tags', 'created', 'last_updated', 'vm_count', 'contacts', 'tags', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'type', 'group', 'status', 'tenant', 'site', 'device_count', 'vm_count') default_columns = ('pk', 'name', 'type', 'group', 'status', 'tenant', 'site', 'device_count', 'vm_count')

View File

@ -2,7 +2,7 @@ import django_tables2 as tables
from dcim.tables.devices import BaseInterfaceTable from dcim.tables.devices import BaseInterfaceTable
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenantColumn from tenancy.tables import TenancyColumnsMixin
from virtualization.models import VirtualMachine, VMInterface from virtualization.models import VirtualMachine, VMInterface
__all__ = ( __all__ = (
@ -24,7 +24,7 @@ VMINTERFACE_BUTTONS = """
# Virtual machines # Virtual machines
# #
class VirtualMachineTable(NetBoxTable): class VirtualMachineTable(TenancyColumnsMixin, NetBoxTable):
name = tables.Column( name = tables.Column(
order_by=('_name',), order_by=('_name',),
linkify=True linkify=True
@ -40,7 +40,6 @@ class VirtualMachineTable(NetBoxTable):
linkify=True linkify=True
) )
role = columns.ColoredLabelColumn() role = columns.ColoredLabelColumn()
tenant = TenantColumn()
comments = columns.MarkdownColumn() comments = columns.MarkdownColumn()
primary_ip4 = tables.Column( primary_ip4 = tables.Column(
linkify=True, linkify=True,
@ -62,8 +61,9 @@ class VirtualMachineTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = VirtualMachine model = VirtualMachine
fields = ( fields = (
'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'tenant_group', 'platform',
'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'tags', 'created', 'last_updated', 'vcpus', 'memory', 'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'tags', 'created',
'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'name', 'status', 'site', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip', 'pk', 'name', 'status', 'site', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',

View File

@ -1,7 +1,7 @@
bleach==5.0.0 bleach==5.0.1
Django==4.0.5 Django==4.0.6
django-cors-headers==3.13.0 django-cors-headers==3.13.0
django-debug-toolbar==3.4.0 django-debug-toolbar==3.5.0
django-filter==22.1 django-filter==22.1
django-graphiql-debug-toolbar==0.2.0 django-graphiql-debug-toolbar==0.2.0
django-mptt==0.13.4 django-mptt==0.13.4
@ -20,13 +20,13 @@ gunicorn==20.1.0
Jinja2==3.1.2 Jinja2==3.1.2
Markdown==3.3.7 Markdown==3.3.7
markdown-include==0.6.0 markdown-include==0.6.0
mkdocs-material==8.3.6 mkdocs-material==8.3.9
mkdocstrings[python-legacy]==0.19.0 mkdocstrings[python-legacy]==0.19.0
netaddr==0.8.0 netaddr==0.8.0
Pillow==9.1.1 Pillow==9.2.0
psycopg2-binary==2.9.3 psycopg2-binary==2.9.3
PyYAML==6.0 PyYAML==6.0
sentry-sdk==1.5.12 sentry-sdk==1.7.0
social-auth-app-django==5.0.0 social-auth-app-django==5.0.0
social-auth-core==4.3.0 social-auth-core==4.3.0
svgwrite==1.4.2 svgwrite==1.4.2