Merge pull request #4555 from netbox-community/492-table-column-ordering

Closes #492: Table column ordering
This commit is contained in:
Jeremy Stretch 2020-05-04 15:12:29 -04:00 committed by GitHub
commit 80f08e6830
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 751 additions and 201 deletions

View File

@ -8,3 +8,4 @@ The `users.UserConfig` model holds individual preferences for each user in the f
| ---- | ----------- | | ---- | ----------- |
| extras.configcontext.format | Preferred format when rendering config context data (JSON or YAML) | | extras.configcontext.format | Preferred format when rendering config context data (JSON or YAML) |
| pagination.per_page | The number of items to display per page of a paginated table | | pagination.per_page | The number of items to display per page of a paginated table |
| tables.${table_name}.columns | The ordered list of columns to display when viewing the table |

View File

@ -27,18 +27,15 @@ STATUS_LABEL = """
class ProviderTable(BaseTable): class ProviderTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
circuit_count = tables.Column(
accessor=Accessor('count_circuits'),
verbose_name='Circuits'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Provider model = Provider
fields = ('pk', 'name', 'asn', 'account',) fields = ('pk', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count')
default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
class ProviderDetailTable(ProviderTable):
circuit_count = tables.Column(accessor=Accessor('count_circuits'), verbose_name='Circuits')
class Meta(ProviderTable.Meta):
model = Provider
fields = ('pk', 'name', 'asn', 'account', 'circuit_count')
# #
@ -58,6 +55,7 @@ class CircuitTypeTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = CircuitType model = CircuitType
fields = ('pk', 'name', 'circuit_count', 'description', 'slug', 'actions') fields = ('pk', 'name', 'circuit_count', 'description', 'slug', 'actions')
default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug', 'actions')
# #
@ -79,4 +77,8 @@ class CircuitTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Circuit model = Circuit
fields = ('pk', 'cid', 'status', 'type', 'provider', 'tenant', 'a_side', 'z_side', 'description') fields = (
'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'install_date', 'commit_rate',
'description',
)
default_columns = ('pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'description')

View File

@ -28,7 +28,7 @@ class ProviderListView(PermissionRequiredMixin, ObjectListView):
queryset = Provider.objects.annotate(count_circuits=Count('circuits')) queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
filterset = filters.ProviderFilterSet filterset = filters.ProviderFilterSet
filterset_form = forms.ProviderFilterForm filterset_form = forms.ProviderFilterForm
table = tables.ProviderDetailTable table = tables.ProviderTable
class ProviderView(PermissionRequiredMixin, View): class ProviderView(PermissionRequiredMixin, View):
@ -87,7 +87,7 @@ class ProviderBulkImportView(PermissionRequiredMixin, BulkImportView):
class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView): class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'circuits.change_provider' permission_required = 'circuits.change_provider'
queryset = Provider.objects.all() queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
filterset = filters.ProviderFilterSet filterset = filters.ProviderFilterSet
table = tables.ProviderTable table = tables.ProviderTable
form = forms.ProviderBulkEditForm form = forms.ProviderBulkEditForm
@ -96,7 +96,7 @@ class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'circuits.delete_provider' permission_required = 'circuits.delete_provider'
queryset = Provider.objects.all() queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
filterset = filters.ProviderFilterSet filterset = filters.ProviderFilterSet
table = tables.ProviderTable table = tables.ProviderTable
default_return_url = 'circuits:provider_list' default_return_url = 'circuits:provider_list'

View File

@ -205,9 +205,13 @@ def get_component_template_actions(model_name):
class RegionTable(BaseTable): class RegionTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.TemplateColumn(template_code=MPTT_LINK, orderable=False) name = tables.TemplateColumn(
site_count = tables.Column(verbose_name='Sites') template_code=MPTT_LINK,
slug = tables.Column(verbose_name='Slug') orderable=False
)
site_count = tables.Column(
verbose_name='Sites'
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=REGION_ACTIONS, template_code=REGION_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -216,7 +220,8 @@ class RegionTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Region model = Region
fields = ('pk', 'name', 'site_count', 'description', 'slug', 'actions') fields = ('pk', 'name', 'slug', 'site_count', 'description', 'actions')
default_columns = ('pk', 'name', 'site_count', 'description', 'actions')
# #
@ -225,14 +230,27 @@ class RegionTable(BaseTable):
class SiteTable(BaseTable): class SiteTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn(order_by=('_name',)) name = tables.LinkColumn(
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status') order_by=('_name',)
region = tables.TemplateColumn(template_code=SITE_REGION_LINK) )
tenant = tables.TemplateColumn(template_code=COL_TENANT) status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
region = tables.TemplateColumn(
template_code=SITE_REGION_LINK
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Site model = Site
fields = ('pk', 'name', 'status', 'facility', 'region', 'tenant', 'asn', 'description') fields = (
'pk', 'name', 'slug', 'status', 'facility', 'region', 'tenant', 'asn', 'time_zone', 'description',
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
'contact_email',
)
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'tenant', 'asn', 'description')
# #
@ -253,7 +271,6 @@ class RackGroupTable(BaseTable):
rack_count = tables.Column( rack_count = tables.Column(
verbose_name='Racks' verbose_name='Racks'
) )
slug = tables.Column()
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=RACKGROUP_ACTIONS, template_code=RACKGROUP_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -263,6 +280,7 @@ class RackGroupTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = RackGroup model = RackGroup
fields = ('pk', 'name', 'site', 'rack_count', 'description', 'slug', 'actions') fields = ('pk', 'name', 'site', 'rack_count', 'description', 'slug', 'actions')
default_columns = ('pk', 'name', 'site', 'rack_count', 'description', 'actions')
# #
@ -282,6 +300,7 @@ class RackRoleTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = RackRole model = RackRole
fields = ('pk', 'name', 'rack_count', 'color', 'description', 'slug', 'actions') fields = ('pk', 'name', 'rack_count', 'color', 'description', 'slug', 'actions')
default_columns = ('pk', 'name', 'rack_count', 'color', 'description', 'actions')
# #
@ -290,17 +309,34 @@ class RackRoleTable(BaseTable):
class RackTable(BaseTable): class RackTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn(order_by=('_name',)) name = tables.LinkColumn(
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) order_by=('_name',)
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group') )
tenant = tables.TemplateColumn(template_code=COL_TENANT) site = tables.LinkColumn(
status = tables.TemplateColumn(STATUS_LABEL) viewname='dcim:site',
role = tables.TemplateColumn(RACK_ROLE) args=[Accessor('site.slug')]
u_height = tables.TemplateColumn("{{ record.u_height }}U", verbose_name='Height') )
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
role = tables.TemplateColumn(
template_code=RACK_ROLE
)
u_height = tables.TemplateColumn(
template_code="{{ record.u_height }}U",
verbose_name='Height'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Rack model = Rack
fields = ('pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'u_height') fields = (
'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
'width', 'u_height',
)
default_columns = ('pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'u_height')
class RackDetailTable(RackTable): class RackDetailTable(RackTable):
@ -321,6 +357,10 @@ class RackDetailTable(RackTable):
class Meta(RackTable.Meta): class Meta(RackTable.Meta):
fields = ( fields = (
'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
'width', 'u_height', 'device_count', 'get_utilization', 'get_power_utilization',
)
default_columns = (
'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count', 'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
'get_utilization', 'get_power_utilization', 'get_utilization', 'get_power_utilization',
) )
@ -364,6 +404,9 @@ class RackReservationTable(BaseTable):
fields = ( fields = (
'pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'actions', 'pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'actions',
) )
default_columns = (
'pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description', 'actions',
)
# #
@ -416,9 +459,12 @@ class DeviceTypeTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = DeviceType model = DeviceType
fields = ( fields = (
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'pk', 'model', 'manufacturer', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
'instance_count', 'instance_count',
) )
default_columns = (
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',
)
# #
@ -427,7 +473,9 @@ class DeviceTypeTable(BaseTable):
class ConsolePortTemplateTable(BaseTable): class ConsolePortTemplateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.Column(order_by=('_name',)) name = tables.Column(
order_by=('_name',)
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=get_component_template_actions('consoleporttemplate'), template_code=get_component_template_actions('consoleporttemplate'),
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -441,7 +489,10 @@ class ConsolePortTemplateTable(BaseTable):
class ConsolePortImportTable(BaseTable): class ConsolePortImportTable(BaseTable):
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device') device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = ConsolePort model = ConsolePort
@ -451,7 +502,9 @@ class ConsolePortImportTable(BaseTable):
class ConsoleServerPortTemplateTable(BaseTable): class ConsoleServerPortTemplateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.Column(order_by=('_name',)) name = tables.Column(
order_by=('_name',)
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=get_component_template_actions('consoleserverporttemplate'), template_code=get_component_template_actions('consoleserverporttemplate'),
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -465,7 +518,10 @@ class ConsoleServerPortTemplateTable(BaseTable):
class ConsoleServerPortImportTable(BaseTable): class ConsoleServerPortImportTable(BaseTable):
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device') device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = ConsoleServerPort model = ConsoleServerPort
@ -475,7 +531,9 @@ class ConsoleServerPortImportTable(BaseTable):
class PowerPortTemplateTable(BaseTable): class PowerPortTemplateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.Column(order_by=('_name',)) name = tables.Column(
order_by=('_name',)
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=get_component_template_actions('powerporttemplate'), template_code=get_component_template_actions('powerporttemplate'),
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -489,7 +547,10 @@ class PowerPortTemplateTable(BaseTable):
class PowerPortImportTable(BaseTable): class PowerPortImportTable(BaseTable):
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device') device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = PowerPort model = PowerPort
@ -499,7 +560,9 @@ class PowerPortImportTable(BaseTable):
class PowerOutletTemplateTable(BaseTable): class PowerOutletTemplateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.Column(order_by=('_name',)) name = tables.Column(
order_by=('_name',)
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=get_component_template_actions('poweroutlettemplate'), template_code=get_component_template_actions('poweroutlettemplate'),
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -513,7 +576,10 @@ class PowerOutletTemplateTable(BaseTable):
class PowerOutletImportTable(BaseTable): class PowerOutletImportTable(BaseTable):
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device') device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = PowerOutlet model = PowerOutlet
@ -523,7 +589,9 @@ class PowerOutletImportTable(BaseTable):
class InterfaceTemplateTable(BaseTable): class InterfaceTemplateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
mgmt_only = tables.TemplateColumn("{% if value %}OOB Management{% endif %}") mgmt_only = tables.TemplateColumn(
template_code="{% if value %}OOB Management{% endif %}"
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=get_component_template_actions('interfacetemplate'), template_code=get_component_template_actions('interfacetemplate'),
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -537,18 +605,30 @@ class InterfaceTemplateTable(BaseTable):
class InterfaceImportTable(BaseTable): class InterfaceImportTable(BaseTable):
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device') device = tables.LinkColumn(
virtual_machine = tables.LinkColumn('virtualization:virtualmachine', args=[Accessor('virtual_machine.pk')], verbose_name='Virtual Machine') viewname='dcim:device',
args=[Accessor('device.pk')]
)
virtual_machine = tables.LinkColumn(
viewname='virtualization:virtualmachine',
args=[Accessor('virtual_machine.pk')],
verbose_name='Virtual Machine'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Interface model = Interface
fields = ('device', 'virtual_machine', 'name', 'description', 'lag', 'type', 'enabled', 'mac_address', 'mtu', 'mgmt_only', 'mode') fields = (
'device', 'virtual_machine', 'name', 'description', 'lag', 'type', 'enabled', 'mac_address', 'mtu',
'mgmt_only', 'mode',
)
empty_text = False empty_text = False
class FrontPortTemplateTable(BaseTable): class FrontPortTemplateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.Column(order_by=('_name',)) name = tables.Column(
order_by=('_name',)
)
rear_port_position = tables.Column( rear_port_position = tables.Column(
verbose_name='Position' verbose_name='Position'
) )
@ -565,7 +645,10 @@ class FrontPortTemplateTable(BaseTable):
class FrontPortImportTable(BaseTable): class FrontPortImportTable(BaseTable):
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device') device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = FrontPort model = FrontPort
@ -575,7 +658,9 @@ class FrontPortImportTable(BaseTable):
class RearPortTemplateTable(BaseTable): class RearPortTemplateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.Column(order_by=('_name',)) name = tables.Column(
order_by=('_name',)
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=get_component_template_actions('rearporttemplate'), template_code=get_component_template_actions('rearporttemplate'),
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -589,7 +674,10 @@ class RearPortTemplateTable(BaseTable):
class RearPortImportTable(BaseTable): class RearPortImportTable(BaseTable):
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device') device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = RearPort model = RearPort
@ -599,7 +687,9 @@ class RearPortImportTable(BaseTable):
class DeviceBayTemplateTable(BaseTable): class DeviceBayTemplateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.Column(order_by=('_name',)) name = tables.Column(
order_by=('_name',)
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=get_component_template_actions('devicebaytemplate'), template_code=get_component_template_actions('devicebaytemplate'),
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -630,8 +720,10 @@ class DeviceRoleTable(BaseTable):
orderable=False, orderable=False,
verbose_name='VMs' verbose_name='VMs'
) )
color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Label') color = tables.TemplateColumn(
slug = tables.Column(verbose_name='Slug') template_code=COLOR_LABEL,
verbose_name='Label'
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=DEVICEROLE_ACTIONS, template_code=DEVICEROLE_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -641,6 +733,7 @@ class DeviceRoleTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = DeviceRole model = DeviceRole
fields = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description', 'slug', 'actions') fields = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description', 'slug', 'actions')
default_columns = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description', 'actions')
# #
@ -670,7 +763,11 @@ class PlatformTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Platform model = Platform
fields = ( fields = (
'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'napalm_driver', 'description', 'actions', 'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'napalm_driver', 'napalm_args',
'description', 'actions',
)
default_columns = (
'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'napalm_driver', 'description', 'actions',
) )
@ -684,40 +781,96 @@ class DeviceTable(BaseTable):
order_by=('_name',), order_by=('_name',),
template_code=DEVICE_LINK template_code=DEVICE_LINK
) )
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status') status = tables.TemplateColumn(
tenant = tables.TemplateColumn(template_code=COL_TENANT) template_code=STATUS_LABEL
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) )
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')]) tenant = tables.TemplateColumn(
device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role') template_code=COL_TENANT
)
site = tables.LinkColumn(
viewname='dcim:site',
args=[Accessor('site.slug')]
)
rack = tables.LinkColumn(
viewname='dcim:rack',
args=[Accessor('rack.pk')]
)
device_role = tables.TemplateColumn(
template_code=DEVICE_ROLE,
verbose_name='Role'
)
device_type = tables.LinkColumn( device_type = tables.LinkColumn(
'dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type', viewname='dcim:devicetype',
args=[Accessor('device_type.pk')],
verbose_name='Type',
text=lambda record: record.device_type.display_name text=lambda record: record.device_type.display_name
) )
primary_ip = tables.TemplateColumn(
template_code=DEVICE_PRIMARY_IP,
orderable=False,
verbose_name='IP Address'
)
primary_ip4 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip4.pk')],
verbose_name='IPv4 Address'
)
primary_ip6 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip6.pk')],
verbose_name='IPv6 Address'
)
cluster = tables.LinkColumn(
viewname='virtualization:cluster',
args=[Accessor('cluster.pk')]
)
virtual_chassis = tables.LinkColumn(
viewname='dcim:virtualchassis',
args=[Accessor('virtual_chassis.pk')]
)
vc_position = tables.Column(
verbose_name='VC Position'
)
vc_priority = tables.Column(
verbose_name='VC Priority'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Device model = Device
fields = ('pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type') fields = (
'pk', 'name', 'status', 'tenant', 'device_role', 'device_type', 'platform', 'serial', 'asset_tag', 'site',
'rack', 'position', 'face', 'primary_ip', 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis',
class DeviceDetailTable(DeviceTable): 'vc_position', 'vc_priority',
primary_ip = tables.TemplateColumn( )
orderable=False, verbose_name='IP Address', template_code=DEVICE_PRIMARY_IP default_columns = (
) 'pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip',
)
class Meta(DeviceTable.Meta):
model = Device
fields = ('pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip')
class DeviceImportTable(BaseTable): class DeviceImportTable(BaseTable):
name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name') name = tables.TemplateColumn(
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status') template_code=DEVICE_LINK
tenant = tables.TemplateColumn(template_code=COL_TENANT) )
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') status = tables.TemplateColumn(
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')], verbose_name='Rack') template_code=STATUS_LABEL
position = tables.Column(verbose_name='Position') )
device_role = tables.Column(verbose_name='Role') tenant = tables.TemplateColumn(
device_type = tables.Column(verbose_name='Type') template_code=COL_TENANT
)
site = tables.LinkColumn(
viewname='dcim:site',
args=[Accessor('site.slug')]
)
rack = tables.LinkColumn(
viewname='dcim:rack',
args=[Accessor('rack.pk')]
)
device_role = tables.Column(
verbose_name='Role'
)
device_type = tables.Column(
verbose_name='Type'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Device model = Device
@ -893,23 +1046,23 @@ class CableTable(BaseTable):
template_code=CABLE_TERMINATION_PARENT, template_code=CABLE_TERMINATION_PARENT,
accessor=Accessor('termination_a'), accessor=Accessor('termination_a'),
orderable=False, orderable=False,
verbose_name='Termination A' verbose_name='Side A'
) )
termination_a = tables.LinkColumn( termination_a = tables.LinkColumn(
accessor=Accessor('termination_a'), accessor=Accessor('termination_a'),
orderable=False, orderable=False,
verbose_name='' verbose_name='Termination A'
) )
termination_b_parent = tables.TemplateColumn( termination_b_parent = tables.TemplateColumn(
template_code=CABLE_TERMINATION_PARENT, template_code=CABLE_TERMINATION_PARENT,
accessor=Accessor('termination_b'), accessor=Accessor('termination_b'),
orderable=False, orderable=False,
verbose_name='Termination B' verbose_name='Side B'
) )
termination_b = tables.LinkColumn( termination_b = tables.LinkColumn(
accessor=Accessor('termination_b'), accessor=Accessor('termination_b'),
orderable=False, orderable=False,
verbose_name='' verbose_name='Termination B'
) )
status = tables.TemplateColumn( status = tables.TemplateColumn(
template_code=STATUS_LABEL template_code=STATUS_LABEL
@ -926,6 +1079,10 @@ class CableTable(BaseTable):
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b', 'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
'status', 'type', 'color', 'length', 'status', 'type', 'color', 'length',
) )
default_columns = (
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
'status', 'type',
)
# #
@ -993,10 +1150,6 @@ class InterfaceConnectionTable(BaseTable):
args=[Accessor('pk')], args=[Accessor('pk')],
verbose_name='Interface A' verbose_name='Interface A'
) )
description_a = tables.Column(
accessor=Accessor('description'),
verbose_name='Description'
)
device_b = tables.LinkColumn( device_b = tables.LinkColumn(
viewname='dcim:device', viewname='dcim:device',
accessor=Accessor('_connected_interface.device'), accessor=Accessor('_connected_interface.device'),
@ -1009,15 +1162,11 @@ class InterfaceConnectionTable(BaseTable):
args=[Accessor('_connected_interface.pk')], args=[Accessor('_connected_interface.pk')],
verbose_name='Interface B' verbose_name='Interface B'
) )
description_b = tables.Column(
accessor=Accessor('_connected_interface.description'),
verbose_name='Description'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Interface model = Interface
fields = ( fields = (
'device_a', 'interface_a', 'description_a', 'device_b', 'interface_b', 'description_b', 'connection_status', 'device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status',
) )
@ -1027,12 +1176,21 @@ class InterfaceConnectionTable(BaseTable):
class InventoryItemTable(BaseTable): class InventoryItemTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
device = tables.LinkColumn('dcim:device_inventory', args=[Accessor('device.pk')]) device = tables.LinkColumn(
manufacturer = tables.Column(accessor=Accessor('manufacturer.name'), verbose_name='Manufacturer') viewname='dcim:device_inventory',
args=[Accessor('device.pk')]
)
manufacturer = tables.Column(
accessor=Accessor('manufacturer.name')
)
discovered = BooleanColumn()
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = InventoryItem model = InventoryItem
fields = ('pk', 'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description') fields = (
'pk', 'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'discovered'
)
default_columns = ('pk', 'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag')
# #
@ -1052,6 +1210,7 @@ class VirtualChassisTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VirtualChassis model = VirtualChassis
fields = ('pk', 'name', 'domain', 'member_count') fields = ('pk', 'name', 'domain', 'member_count')
default_columns = ('pk', 'name', 'domain', 'member_count')
# #
@ -1073,6 +1232,7 @@ class PowerPanelTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = PowerPanel model = PowerPanel
fields = ('pk', 'name', 'site', 'rack_group', 'powerfeed_count') fields = ('pk', 'name', 'site', 'rack_group', 'powerfeed_count')
default_columns = ('pk', 'name', 'site', 'rack_group', 'powerfeed_count')
# #
@ -1096,7 +1256,19 @@ class PowerFeedTable(BaseTable):
type = tables.TemplateColumn( type = tables.TemplateColumn(
template_code=TYPE_LABEL template_code=TYPE_LABEL
) )
max_utilization = tables.TemplateColumn(
template_code="{{ value }}%"
)
available_power = tables.Column(
verbose_name='Available power (VA)'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = PowerFeed model = PowerFeed
fields = ('pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase') fields = (
'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
'max_utilization', 'available_power',
)
default_columns = (
'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
)

View File

@ -1095,7 +1095,7 @@ class DeviceListView(PermissionRequiredMixin, ObjectListView):
) )
filterset = filters.DeviceFilterSet filterset = filters.DeviceFilterSet
filterset_form = forms.DeviceFilterForm filterset_form = forms.DeviceFilterForm
table = tables.DeviceDetailTable table = tables.DeviceTable
template_name = 'dcim/device_list.html' template_name = 'dcim/device_list.html'
@ -2278,19 +2278,15 @@ class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView):
csv_data = [ csv_data = [
# Headers # Headers
','.join([ ','.join([
'device_a', 'interface_a', 'interface_a_description', 'device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status'
'device_b', 'interface_b', 'interface_b_description',
'connection_status'
]) ])
] ]
for obj in self.queryset: for obj in self.queryset:
csv = csv_format([ csv = csv_format([
obj.connected_endpoint.device.identifier if obj.connected_endpoint else None, obj.connected_endpoint.device.identifier if obj.connected_endpoint else None,
obj.connected_endpoint.name if obj.connected_endpoint else None, obj.connected_endpoint.name if obj.connected_endpoint else None,
obj.connected_endpoint.description if obj.connected_endpoint else None,
obj.device.identifier, obj.device.identifier,
obj.name, obj.name,
obj.description,
obj.get_connection_status_display(), obj.get_connection_status_display(),
]) ])
csv_data.append(csv) csv_data.append(csv)

View File

@ -104,7 +104,11 @@ class ConfigContextTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = ConfigContext model = ConfigContext
fields = ('pk', 'name', 'weight', 'is_active', 'description') fields = (
'pk', 'name', 'weight', 'is_active', 'description', 'regions', 'sites', 'roles', 'platforms',
'cluster_groups', 'clusters', 'tenant_groups', 'tenants',
)
default_columns = ('pk', 'name', 'weight', 'is_active', 'description')
class ObjectChangeTable(BaseTable): class ObjectChangeTable(BaseTable):

View File

@ -190,12 +190,20 @@ TENANT_LINK = """
class VRFTable(BaseTable): class VRFTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
rd = tables.Column(verbose_name='RD') rd = tables.Column(
tenant = tables.TemplateColumn(template_code=COL_TENANT) verbose_name='RD'
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
enforce_unique = BooleanColumn(
verbose_name='Unique'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VRF model = VRF
fields = ('pk', 'name', 'rd', 'tenant', 'description') fields = ('pk', 'name', 'rd', 'tenant', 'enforce_unique', 'description')
default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
# #
@ -204,14 +212,23 @@ class VRFTable(BaseTable):
class RIRTable(BaseTable): class RIRTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name') name = tables.LinkColumn()
is_private = BooleanColumn(verbose_name='Private') is_private = BooleanColumn(
aggregate_count = tables.Column(verbose_name='Aggregates') verbose_name='Private'
actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name='') )
aggregate_count = tables.Column(
verbose_name='Aggregates'
)
actions = tables.TemplateColumn(
template_code=RIR_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = RIR model = RIR
fields = ('pk', 'name', 'is_private', 'aggregate_count', 'description', 'actions') fields = ('pk', 'name', 'slug', 'is_private', 'aggregate_count', 'description', 'actions')
default_columns = ('pk', 'name', 'is_private', 'aggregate_count', 'description', 'actions')
class RIRDetailTable(RIRTable): class RIRDetailTable(RIRTable):
@ -247,6 +264,10 @@ class RIRDetailTable(RIRTable):
class Meta(RIRTable.Meta): class Meta(RIRTable.Meta):
fields = ( fields = (
'pk', 'name', 'slug', 'is_private', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved',
'stats_deprecated', 'stats_available', 'utilization', 'actions',
)
default_columns = (
'pk', 'name', 'is_private', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved', 'pk', 'name', 'is_private', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved',
'stats_deprecated', 'stats_available', 'utilization', 'actions', 'stats_deprecated', 'stats_available', 'utilization', 'actions',
) )
@ -258,8 +279,13 @@ class RIRDetailTable(RIRTable):
class AggregateTable(BaseTable): class AggregateTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
prefix = tables.LinkColumn(verbose_name='Aggregate') prefix = tables.LinkColumn(
date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added') verbose_name='Aggregate'
)
date_added = tables.DateColumn(
format="Y-m-d",
verbose_name='Added'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Aggregate model = Aggregate
@ -267,8 +293,13 @@ class AggregateTable(BaseTable):
class AggregateDetailTable(AggregateTable): class AggregateDetailTable(AggregateTable):
child_count = tables.Column(verbose_name='Prefixes') child_count = tables.Column(
utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization') verbose_name='Prefixes'
)
utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
orderable=False
)
class Meta(AggregateTable.Meta): class Meta(AggregateTable.Meta):
fields = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description') fields = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description')
@ -300,7 +331,8 @@ class RoleTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Role model = Role
fields = ('pk', 'name', 'prefix_count', 'vlan_count', 'description', 'slug', 'weight', 'actions') fields = ('pk', 'name', 'slug', 'prefix_count', 'vlan_count', 'description', 'weight', 'actions')
default_columns = ('pk', 'name', 'prefix_count', 'vlan_count', 'description', 'actions')
# #
@ -309,28 +341,61 @@ class RoleTable(BaseTable):
class PrefixTable(BaseTable): class PrefixTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
prefix = tables.TemplateColumn(PREFIX_LINK, attrs={'th': {'style': 'padding-left: 17px'}}) prefix = tables.TemplateColumn(
status = tables.TemplateColumn(STATUS_LABEL) template_code=PREFIX_LINK,
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF') attrs={'th': {'style': 'padding-left: 17px'}}
tenant = tables.TemplateColumn(template_code=TENANT_LINK) )
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) status = tables.TemplateColumn(
vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN') template_code=STATUS_LABEL
role = tables.TemplateColumn(PREFIX_ROLE_LINK) )
vrf = tables.TemplateColumn(
template_code=VRF_LINK,
verbose_name='VRF'
)
tenant = tables.TemplateColumn(
template_code=TENANT_LINK
)
site = tables.LinkColumn(
viewname='dcim:site',
args=[Accessor('site.slug')]
)
vlan = tables.LinkColumn(
viewname='ipam:vlan',
args=[Accessor('vlan.pk')],
verbose_name='VLAN'
)
role = tables.TemplateColumn(
template_code=PREFIX_ROLE_LINK
)
is_pool = BooleanColumn(
verbose_name='Pool'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Prefix model = Prefix
fields = ('pk', 'prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'description') fields = ('pk', 'prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'is_pool', 'description')
default_columns = ('pk', 'prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'description')
row_attrs = { row_attrs = {
'class': lambda record: 'success' if not record.pk else '', 'class': lambda record: 'success' if not record.pk else '',
} }
class PrefixDetailTable(PrefixTable): class PrefixDetailTable(PrefixTable):
utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False) utilization = tables.TemplateColumn(
tenant = tables.TemplateColumn(template_code=COL_TENANT) template_code=UTILIZATION_GRAPH,
orderable=False
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
class Meta(PrefixTable.Meta): class Meta(PrefixTable.Meta):
fields = ('pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description') fields = (
'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'is_pool', 'description',
)
default_columns = (
'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
)
# #
@ -339,12 +404,27 @@ class PrefixDetailTable(PrefixTable):
class IPAddressTable(BaseTable): class IPAddressTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address') address = tables.TemplateColumn(
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF') template_code=IPADDRESS_LINK,
status = tables.TemplateColumn(STATUS_LABEL) verbose_name='IP Address'
tenant = tables.TemplateColumn(template_code=TENANT_LINK) )
parent = tables.TemplateColumn(IPADDRESS_PARENT, orderable=False) vrf = tables.TemplateColumn(
interface = tables.Column(orderable=False) template_code=VRF_LINK,
verbose_name='VRF'
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
tenant = tables.TemplateColumn(
template_code=TENANT_LINK
)
parent = tables.TemplateColumn(
template_code=IPADDRESS_PARENT,
orderable=False
)
interface = tables.Column(
orderable=False
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = IPAddress model = IPAddress
@ -358,22 +438,40 @@ class IPAddressTable(BaseTable):
class IPAddressDetailTable(IPAddressTable): class IPAddressDetailTable(IPAddressTable):
nat_inside = tables.LinkColumn( nat_inside = tables.LinkColumn(
'ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False, verbose_name='NAT (Inside)' viewname='ipam:ipaddress',
args=[Accessor('nat_inside.pk')],
orderable=False,
verbose_name='NAT (Inside)'
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
) )
tenant = tables.TemplateColumn(template_code=COL_TENANT)
class Meta(IPAddressTable.Meta): class Meta(IPAddressTable.Meta):
fields = ( fields = (
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'parent', 'interface', 'dns_name', 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'parent', 'interface', 'dns_name',
'description', 'description',
) )
default_columns = (
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'dns_name', 'description',
)
class IPAddressAssignTable(BaseTable): class IPAddressAssignTable(BaseTable):
address = tables.TemplateColumn(IPADDRESS_ASSIGN_LINK, verbose_name='IP Address') address = tables.TemplateColumn(
status = tables.TemplateColumn(STATUS_LABEL) template_code=IPADDRESS_ASSIGN_LINK,
parent = tables.TemplateColumn(IPADDRESS_PARENT, orderable=False) verbose_name='IP Address'
interface = tables.Column(orderable=False) )
status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
parent = tables.TemplateColumn(
template_code=IPADDRESS_PARENT,
orderable=False
)
interface = tables.Column(
orderable=False
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = IPAddress model = IPAddress
@ -385,10 +483,19 @@ class InterfaceIPAddressTable(BaseTable):
""" """
List IP addresses assigned to a specific Interface. List IP addresses assigned to a specific Interface.
""" """
address = tables.LinkColumn(verbose_name='IP Address') address = tables.LinkColumn(
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF') verbose_name='IP Address'
status = tables.TemplateColumn(STATUS_LABEL) )
tenant = tables.TemplateColumn(template_code=TENANT_LINK) vrf = tables.TemplateColumn(
template_code=VRF_LINK,
verbose_name='VRF'
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
tenant = tables.TemplateColumn(
template_code=TENANT_LINK
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = IPAddress model = IPAddress
@ -401,16 +508,24 @@ class InterfaceIPAddressTable(BaseTable):
class VLANGroupTable(BaseTable): class VLANGroupTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name') name = tables.LinkColumn()
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') site = tables.LinkColumn(
vlan_count = tables.Column(verbose_name='VLANs') viewname='dcim:site',
slug = tables.Column(verbose_name='Slug') args=[Accessor('site.slug')]
actions = tables.TemplateColumn(template_code=VLANGROUP_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, )
verbose_name='') vlan_count = tables.Column(
verbose_name='VLANs'
)
actions = tables.TemplateColumn(
template_code=VLANGROUP_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VLANGroup model = VLANGroup
fields = ('pk', 'name', 'site', 'vlan_count', 'slug', 'description', 'actions') fields = ('pk', 'name', 'site', 'vlan_count', 'slug', 'description', 'actions')
default_columns = ('pk', 'name', 'site', 'vlan_count', 'description', 'actions')
# #
@ -419,12 +534,27 @@ class VLANGroupTable(BaseTable):
class VLANTable(BaseTable): class VLANTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
vid = tables.TemplateColumn(VLAN_LINK, verbose_name='ID') vid = tables.TemplateColumn(
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) template_code=VLAN_LINK,
group = tables.LinkColumn('ipam:vlangroup_vlans', args=[Accessor('group.pk')], verbose_name='Group') verbose_name='ID'
tenant = tables.TemplateColumn(template_code=COL_TENANT) )
status = tables.TemplateColumn(STATUS_LABEL) site = tables.LinkColumn(
role = tables.TemplateColumn(VLAN_ROLE_LINK) viewname='dcim:site',
args=[Accessor('site.slug')]
)
group = tables.LinkColumn(
viewname='ipam:vlangroup_vlans',
args=[Accessor('group.pk')]
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
role = tables.TemplateColumn(
template_code=VLAN_ROLE_LINK
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VLAN model = VLAN
@ -435,16 +565,26 @@ class VLANTable(BaseTable):
class VLANDetailTable(VLANTable): class VLANDetailTable(VLANTable):
prefixes = tables.TemplateColumn(VLAN_PREFIXES, orderable=False, verbose_name='Prefixes') prefixes = tables.TemplateColumn(
tenant = tables.TemplateColumn(template_code=COL_TENANT) template_code=VLAN_PREFIXES,
orderable=False,
verbose_name='Prefixes'
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
class Meta(VLANTable.Meta): class Meta(VLANTable.Meta):
fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description') fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
class VLANMemberTable(BaseTable): class VLANMemberTable(BaseTable):
parent = tables.LinkColumn(order_by=['device', 'virtual_machine']) parent = tables.LinkColumn(
name = tables.LinkColumn(verbose_name='Interface') order_by=['device', 'virtual_machine']
)
name = tables.LinkColumn(
verbose_name='Interface'
)
untagged = tables.TemplateColumn( untagged = tables.TemplateColumn(
template_code=VLAN_MEMBER_UNTAGGED, template_code=VLAN_MEMBER_UNTAGGED,
orderable=False orderable=False
@ -464,13 +604,29 @@ class InterfaceVLANTable(BaseTable):
""" """
List VLANs assigned to a specific Interface. List VLANs assigned to a specific Interface.
""" """
vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID') vid = tables.LinkColumn(
viewname='ipam:vlan',
args=[Accessor('pk')],
verbose_name='ID'
)
tagged = BooleanColumn() tagged = BooleanColumn()
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) site = tables.LinkColumn(
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group') viewname='dcim:site',
tenant = tables.TemplateColumn(template_code=COL_TENANT) args=[Accessor('site.slug')]
status = tables.TemplateColumn(STATUS_LABEL) )
role = tables.TemplateColumn(VLAN_ROLE_LINK) group = tables.Column(
accessor=Accessor('group.name'),
verbose_name='Group'
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
role = tables.TemplateColumn(
template_code=VLAN_ROLE_LINK
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VLAN model = VLAN
@ -494,4 +650,5 @@ class ServiceTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Service model = Service
fields = ('pk', 'name', 'parent', 'protocol', 'port', 'description') fields = ('pk', 'name', 'parent', 'protocol', 'port', 'ipaddresses', 'description')
default_columns = ('pk', 'name', 'parent', 'protocol', 'port', 'description')

View File

@ -20,7 +20,7 @@ from dcim.models import (
Cable, ConsolePort, Device, DeviceType, Interface, PowerPanel, PowerFeed, PowerPort, Rack, RackGroup, Site, VirtualChassis Cable, ConsolePort, Device, DeviceType, Interface, PowerPanel, PowerFeed, PowerPort, Rack, RackGroup, Site, VirtualChassis
) )
from dcim.tables import ( from dcim.tables import (
CableTable, DeviceDetailTable, DeviceTypeTable, PowerFeedTable, RackTable, RackGroupTable, SiteTable, CableTable, DeviceTable, DeviceTypeTable, PowerFeedTable, RackTable, RackGroupTable, SiteTable,
VirtualChassisTable, VirtualChassisTable,
) )
from extras.models import ObjectChange, ReportResult from extras.models import ObjectChange, ReportResult
@ -44,7 +44,7 @@ SEARCH_TYPES = OrderedDict((
# Circuits # Circuits
('provider', { ('provider', {
'permission': 'circuits.view_provider', 'permission': 'circuits.view_provider',
'queryset': Provider.objects.all(), 'queryset': Provider.objects.annotate(count_circuits=Count('circuits')),
'filterset': ProviderFilterSet, 'filterset': ProviderFilterSet,
'table': ProviderTable, 'table': ProviderTable,
'url': 'circuits:provider_list', 'url': 'circuits:provider_list',
@ -93,7 +93,7 @@ SEARCH_TYPES = OrderedDict((
'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6', 'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6',
), ),
'filterset': DeviceFilterSet, 'filterset': DeviceFilterSet,
'table': DeviceDetailTable, 'table': DeviceTable,
'url': 'dcim:device_list', 'url': 'dcim:device_list',
}), }),
('virtualchassis', { ('virtualchassis', {

View File

@ -448,4 +448,33 @@ $(document).ready(function() {
$('a.image-preview').on('mouseout', function() { $('a.image-preview').on('mouseout', function() {
$('#image-preview-window').fadeOut('fast'); $('#image-preview-window').fadeOut('fast');
}); });
// Rearrange options within a <select> list
$('#move-option-up').bind('click', function() {
var select_id = '#' + $(this).attr('data-target');
$(select_id + ' option:selected').each(function () {
var newPos = $(select_id + ' option').index(this) - 1;
if (newPos > -1) {
$(select_id + ' option').eq(newPos).before("<option value='" + $(this).val() + "' selected='selected'>" + $(this).text() + "</option>");
$(this).remove();
}
});
});
$('#move-option-down').bind('click', function() {
var select_id = '#' + $(this).attr('data-target');
var countOptions = $(select_id + ' option').length;
var countSelectedOptions = $(select_id + ' option:selected').length;
$(select_id + ' option:selected').each(function () {
var newPos = $(select_id + ' option').index(this) + countSelectedOptions;
if (newPos < countOptions) {
$(select_id + ' option').eq(newPos).after("<option value='" + $(this).val() + "' selected='selected'>" + $(this).text() + "</option>");
$(this).remove();
}
});
});
$('#select-all-options').bind('click', function() {
var select_id = '#' + $(this).attr('data-target');
$(select_id + ' option').prop('selected',true);
});
}); });

View File

@ -20,14 +20,19 @@ SECRETROLE_ACTIONS = """
class SecretRoleTable(BaseTable): class SecretRoleTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
secret_count = tables.Column(verbose_name='Secrets') secret_count = tables.Column(
verbose_name='Secrets'
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=SECRETROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name='' template_code=SECRETROLE_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
) )
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = SecretRole model = SecretRole
fields = ('pk', 'name', 'secret_count', 'description', 'slug', 'actions') fields = ('pk', 'name', 'secret_count', 'description', 'slug', 'users', 'groups', 'actions')
default_columns = ('pk', 'name', 'secret_count', 'description', 'actions')
# #
@ -40,4 +45,5 @@ class SecretTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Secret model = Secret
fields = ('pk', 'device', 'role', 'name', 'last_updated') fields = ('pk', 'device', 'role', 'name', 'last_updated', 'hash')
default_columns = ('pk', 'device', 'role', 'name', 'last_updated')

View File

@ -0,0 +1,28 @@
{% load form_helpers %}
<div class="modal fade" tabindex="-1" id="tableconfig">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Table Configuration</h4>
</div>
<div class="modal-body">
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
{% render_form table_config_form %}
<div class="row">
<div class="col-md-9 col-md-offset-3">
<a class="btn btn-primary btn-xs" id="move-option-up" data-target="id_columns"><i class="fa fa-arrow-up"></i> Move up</a>
<a class="btn btn-primary btn-xs" id="move-option-down" data-target="id_columns"><i class="fa fa-arrow-down"></i> Move down</a>
<a class="btn btn-success btn-xs" id="select-all-options" data-target="id_columns"><i class="fa fa-ellipsis-v"></i> Select all</a>
</div>
</div>
<div class="text-right">
<input type="submit" class="btn btn-primary" name="set" value="Save" />
<input type="submit" class="btn btn-danger" name="clear" value="Reset" />
</div>
</form>
</div>
</div>
</div>
</div>

View File

@ -5,6 +5,9 @@
{% block content %} {% block content %}
<div class="pull-right noprint"> <div class="pull-right noprint">
{% block buttons %}{% endblock %} {% block buttons %}{% endblock %}
{% if table_config_form %}
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#tableconfig" title="Configure table"><i class="fa fa-cog"></i> Configure</button>
{% endif %}
{% if permissions.add and 'add' in action_buttons %} {% if permissions.add and 'add' in action_buttons %}
{% add_button content_type.model_class|url_name:"add" %} {% add_button content_type.model_class|url_name:"add" %}
{% endif %} {% endif %}
@ -68,6 +71,9 @@
{% endwith %} {% endwith %}
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
<div class="clearfix"></div> <div class="clearfix"></div>
{% if table_config_form %}
{% include 'inc/table_config_form.html' %}
{% endif %}
</div> </div>
{% if filter_form %} {% if filter_form %}
<div class="col-md-3 noprint"> <div class="col-md-3 noprint">

View File

@ -44,7 +44,6 @@ class TenantGroupTable(BaseTable):
tenant_count = tables.Column( tenant_count = tables.Column(
verbose_name='Tenants' verbose_name='Tenants'
) )
slug = tables.Column()
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=TENANTGROUP_ACTIONS, template_code=TENANTGROUP_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -54,6 +53,7 @@ class TenantGroupTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = TenantGroup model = TenantGroup
fields = ('pk', 'name', 'tenant_count', 'description', 'slug', 'actions') fields = ('pk', 'name', 'tenant_count', 'description', 'slug', 'actions')
default_columns = ('pk', 'name', 'tenant_count', 'description', 'actions')
# #
@ -66,4 +66,5 @@ class TenantTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Tenant model = Tenant
fields = ('pk', 'name', 'group', 'description') fields = ('pk', 'name', 'slug', 'group', 'description')
default_columns = ('pk', 'name', 'group', 'description')

View File

@ -108,7 +108,7 @@ class UserConfig(models.Model):
userconfig.clear('foo.bar.baz') userconfig.clear('foo.bar.baz')
A KeyError is raised in the event any key along the path does not exist. Invalid keys will be ignored silently.
:param path: Dotted path to the configuration key. For example, 'foo.bar' deletes self.data['foo']['bar']. :param path: Dotted path to the configuration key. For example, 'foo.bar' deletes self.data['foo']['bar'].
:param commit: If true, the UserConfig instance will be saved once the new value has been applied. :param commit: If true, the UserConfig instance will be saved once the new value has been applied.
@ -117,11 +117,13 @@ class UserConfig(models.Model):
keys = path.split('.') keys = path.split('.')
for key in keys[:-1]: for key in keys[:-1]:
if key in d and type(d[key]) is dict: if key not in d:
break
if type(d[key]) is dict:
d = d[key] d = d[key]
key = keys[-1] key = keys[-1]
del(d[key]) d.pop(key, None) # Avoid a KeyError on invalid keys
if commit: if commit:
self.save() self.save()

View File

View File

@ -104,6 +104,5 @@ class UserConfigTest(TestCase):
self.assertTrue('foo' not in userconfig.data['b']) self.assertTrue('foo' not in userconfig.data['b'])
self.assertEqual(userconfig.data['b']['bar'], 102) self.assertEqual(userconfig.data['b']['bar'], 102)
# Clear an invalid value # Clear a non-existing value; should fail silently
with self.assertRaises(KeyError): userconfig.clear('invalid')
userconfig.clear('invalid')

View File

@ -665,7 +665,10 @@ class BootstrapMixin(forms.BaseForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
exempt_widgets = [ exempt_widgets = [
forms.CheckboxInput, forms.ClearableFileInput, forms.FileInput, forms.RadioSelect forms.CheckboxInput,
forms.ClearableFileInput,
forms.FileInput,
forms.RadioSelect
] ]
for field_name, field in self.fields.items(): for field_name, field in self.fields.items():
@ -752,3 +755,23 @@ class ImportForm(BootstrapMixin, forms.Form):
raise forms.ValidationError({ raise forms.ValidationError({
'data': "Invalid YAML data: {}".format(err) 'data': "Invalid YAML data: {}".format(err)
}) })
class TableConfigForm(BootstrapMixin, forms.Form):
"""
Form for configuring user's table preferences.
"""
columns = forms.MultipleChoiceField(
choices=[],
widget=forms.SelectMultiple(
attrs={'size': 10}
),
help_text="Use the buttons below to arrange columns in the desired order, then select all columns to display."
)
def __init__(self, table, *args, **kwargs):
super().__init__(*args, **kwargs)
# Initialize columns field based on table attributes
self.fields['columns'].choices = table.configurable_columns
self.fields['columns'].initial = table.visible_columns

View File

@ -1,4 +1,7 @@
import django_tables2 as tables import django_tables2 as tables
from django.core.exceptions import FieldDoesNotExist
from django.db.models import ForeignKey
from django_tables2.data import TableQuerysetData
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -6,17 +9,73 @@ class BaseTable(tables.Table):
""" """
Default table for object lists Default table for object lists
""" """
def __init__(self, *args, **kwargs): class Meta:
attrs = {
'class': 'table table-hover table-headings',
}
def __init__(self, *args, columns=None, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# Set default empty_text if none was provided # Set default empty_text if none was provided
if self.empty_text is None: if self.empty_text is None:
self.empty_text = 'No {} found'.format(self._meta.model._meta.verbose_name_plural) self.empty_text = 'No {} found'.format(self._meta.model._meta.verbose_name_plural)
class Meta: # Hide non-default columns
attrs = { default_columns = getattr(self.Meta, 'default_columns', list())
'class': 'table table-hover table-headings', if default_columns:
} for column in self.columns:
if column.name not in default_columns:
self.columns.hide(column.name)
# Apply custom column ordering
if columns is not None:
pk = self.base_columns.pop('pk', None)
actions = self.base_columns.pop('actions', None)
for name, column in self.base_columns.items():
if name in columns:
self.columns.show(name)
else:
self.columns.hide(name)
self.sequence = columns
# Always include PK and actions column, if defined on the table
if pk:
self.base_columns['pk'] = pk
self.sequence.insert(0, 'pk')
if actions:
self.base_columns['actions'] = actions
self.sequence.append('actions')
# Dynamically update the table's QuerySet to ensure related fields are pre-fetched
if isinstance(self.data, TableQuerysetData):
model = getattr(self.Meta, 'model')
prefetch_fields = []
for column in self.columns:
if column.visible:
field_path = column.accessor.split('.')
try:
model_field = model._meta.get_field(field_path[0])
if isinstance(model_field, ForeignKey):
prefetch_fields.append('__'.join(field_path))
except FieldDoesNotExist:
pass
self.data.data = self.data.data.prefetch_related(None).prefetch_related(*prefetch_fields)
@property
def configurable_columns(self):
selected_columns = [
(name, self.columns[name].verbose_name) for name in self.sequence if name not in ['pk', 'actions']
]
available_columns = [
(name, column.verbose_name) for name, column in self.columns.items() if name not in self.sequence and name not in ['pk', 'actions']
]
return selected_columns + available_columns
@property
def visible_columns(self):
return [name for name in self.sequence if self.columns[name].visible]
class ToggleColumn(tables.CheckBoxColumn): class ToggleColumn(tables.CheckBoxColumn):

View File

@ -24,7 +24,7 @@ from django_tables2 import RequestConfig
from extras.models import CustomField, CustomFieldValue, ExportTemplate from extras.models import CustomField, CustomFieldValue, ExportTemplate
from extras.querysets import CustomFieldQueryset from extras.querysets import CustomFieldQueryset
from utilities.exceptions import AbortTransaction from utilities.exceptions import AbortTransaction
from utilities.forms import BootstrapMixin, CSVDataField from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm
from utilities.utils import csv_format, prepare_cloned_fields from utilities.utils import csv_format, prepare_cloned_fields
from .error_handlers import handle_protectederror from .error_handlers import handle_protectederror
from .forms import ConfirmationForm, ImportForm from .forms import ConfirmationForm, ImportForm
@ -164,7 +164,8 @@ class ObjectListView(View):
permissions[action] = request.user.has_perm(perm_name) permissions[action] = request.user.has_perm(perm_name)
# Construct the table based on the user's permissions # Construct the table based on the user's permissions
table = self.table(self.queryset) columns = request.user.config.get(f"tables.{self.table.__name__}.columns")
table = self.table(self.queryset, columns=columns)
if 'pk' in table.base_columns and (permissions['change'] or permissions['delete']): if 'pk' in table.base_columns and (permissions['change'] or permissions['delete']):
table.columns.show('pk') table.columns.show('pk')
@ -180,12 +181,29 @@ class ObjectListView(View):
'table': table, 'table': table,
'permissions': permissions, 'permissions': permissions,
'action_buttons': self.action_buttons, 'action_buttons': self.action_buttons,
'table_config_form': TableConfigForm(table=table),
'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None, 'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
} }
context.update(self.extra_context()) context.update(self.extra_context())
return render(request, self.template_name, context) return render(request, self.template_name, context)
def post(self, request):
# Update the user's table configuration
table = self.table(self.queryset)
form = TableConfigForm(table=table, data=request.POST)
preference_name = f"tables.{self.table.__name__}.columns"
if form.is_valid():
if 'set' in request.POST:
request.user.config.set(preference_name, form.cleaned_data['columns'], commit=True)
elif 'clear' in request.POST:
request.user.config.clear(preference_name, commit=True)
messages.success(request, "Your preferences have been updated.")
return redirect(request.get_full_path())
def alter_queryset(self, request): def alter_queryset(self, request):
# .all() is necessary to avoid caching queries # .all() is necessary to avoid caching queries
return self.queryset.all() return self.queryset.all()

View File

@ -46,7 +46,9 @@ VIRTUALMACHINE_PRIMARY_IP = """
class ClusterTypeTable(BaseTable): class ClusterTypeTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
cluster_count = tables.Column(verbose_name='Clusters') cluster_count = tables.Column(
verbose_name='Clusters'
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=CLUSTERTYPE_ACTIONS, template_code=CLUSTERTYPE_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -55,7 +57,8 @@ class ClusterTypeTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = ClusterType model = ClusterType
fields = ('pk', 'name', 'cluster_count', 'description', 'actions') fields = ('pk', 'name', 'slug', 'cluster_count', 'description', 'actions')
default_columns = ('pk', 'name', 'cluster_count', 'description', 'actions')
# #
@ -65,7 +68,9 @@ class ClusterTypeTable(BaseTable):
class ClusterGroupTable(BaseTable): class ClusterGroupTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
cluster_count = tables.Column(verbose_name='Clusters') cluster_count = tables.Column(
verbose_name='Clusters'
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=CLUSTERGROUP_ACTIONS, template_code=CLUSTERGROUP_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -74,7 +79,8 @@ class ClusterGroupTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = ClusterGroup model = ClusterGroup
fields = ('pk', 'name', 'cluster_count', 'description', 'actions') fields = ('pk', 'name', 'slug', 'cluster_count', 'description', 'actions')
default_columns = ('pk', 'name', 'cluster_count', 'description', 'actions')
# #
@ -84,10 +90,24 @@ class ClusterGroupTable(BaseTable):
class ClusterTable(BaseTable): class ClusterTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') tenant = tables.LinkColumn(
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) viewname='tenancy:tenant',
device_count = tables.Column(accessor=Accessor('devices.count'), orderable=False, verbose_name='Devices') args=[Accessor('tenant.slug')]
vm_count = tables.Column(accessor=Accessor('virtual_machines.count'), orderable=False, verbose_name='VMs') )
site = tables.LinkColumn(
viewname='dcim:site',
args=[Accessor('site.slug')]
)
device_count = tables.Column(
accessor=Accessor('devices.count'),
orderable=False,
verbose_name='Devices'
)
vm_count = tables.Column(
accessor=Accessor('virtual_machines.count'),
orderable=False,
verbose_name='VMs'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Cluster model = Cluster
@ -101,10 +121,19 @@ class ClusterTable(BaseTable):
class VirtualMachineTable(BaseTable): class VirtualMachineTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
status = tables.TemplateColumn(template_code=VIRTUALMACHINE_STATUS) status = tables.TemplateColumn(
cluster = tables.LinkColumn('virtualization:cluster', args=[Accessor('cluster.pk')]) template_code=VIRTUALMACHINE_STATUS
role = tables.TemplateColumn(VIRTUALMACHINE_ROLE) )
tenant = tables.TemplateColumn(template_code=COL_TENANT) cluster = tables.LinkColumn(
viewname='virtualization:cluster',
args=[Accessor('cluster.pk')]
)
role = tables.TemplateColumn(
template_code=VIRTUALMACHINE_ROLE
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VirtualMachine model = VirtualMachine
@ -112,13 +141,31 @@ class VirtualMachineTable(BaseTable):
class VirtualMachineDetailTable(VirtualMachineTable): class VirtualMachineDetailTable(VirtualMachineTable):
primary_ip4 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip4.pk')],
verbose_name='IPv4 Address'
)
primary_ip6 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip6.pk')],
verbose_name='IPv6 Address'
)
primary_ip = tables.TemplateColumn( primary_ip = tables.TemplateColumn(
orderable=False, verbose_name='IP Address', template_code=VIRTUALMACHINE_PRIMARY_IP orderable=False,
verbose_name='IP Address',
template_code=VIRTUALMACHINE_PRIMARY_IP
) )
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VirtualMachine model = VirtualMachine
fields = ('pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip') fields = (
'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'primary_ip4',
'primary_ip6', 'primary_ip',
)
default_columns = (
'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',
)
# #