1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Closes #3064: Include tags in object lists as a toggleable table column

This commit is contained in:
Jeremy Stretch
2020-05-06 14:42:51 -04:00
parent fbc8b46d13
commit a4dbd2dae5
9 changed files with 126 additions and 34 deletions

View File

@ -6,6 +6,7 @@
* [#492](https://github.com/netbox-community/netbox/issues/492) - Enable toggling and rearranging table columns * [#492](https://github.com/netbox-community/netbox/issues/492) - Enable toggling and rearranging table columns
* [#3147](https://github.com/netbox-community/netbox/issues/3147) - Allow specifying related objects by arbitrary attribute during CSV import * [#3147](https://github.com/netbox-community/netbox/issues/3147) - Allow specifying related objects by arbitrary attribute during CSV import
* [#3064](https://github.com/netbox-community/netbox/issues/3064) - Include tags in object lists as a toggleable table column
* [#3294](https://github.com/netbox-community/netbox/issues/3294) - Implement mechanism for storing user preferences * [#3294](https://github.com/netbox-community/netbox/issues/3294) - Implement mechanism for storing user preferences
* [#4421](https://github.com/netbox-community/netbox/issues/4421) - Retain user's preference for config context format * [#4421](https://github.com/netbox-community/netbox/issues/4421) - Retain user's preference for config context format
* [#4502](https://github.com/netbox-community/netbox/issues/4502) - Enable configuration of proxies for outbound HTTP requests * [#4502](https://github.com/netbox-community/netbox/issues/4502) - Enable configuration of proxies for outbound HTTP requests

View File

@ -2,7 +2,7 @@ import django_tables2 as tables
from django_tables2.utils import Accessor from django_tables2.utils import Accessor
from tenancy.tables import COL_TENANT from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, ToggleColumn from utilities.tables import BaseTable, TagColumn, ToggleColumn
from .models import Circuit, CircuitType, Provider from .models import Circuit, CircuitType, Provider
CIRCUITTYPE_ACTIONS = """ CIRCUITTYPE_ACTIONS = """
@ -31,10 +31,15 @@ class ProviderTable(BaseTable):
accessor=Accessor('count_circuits'), accessor=Accessor('count_circuits'),
verbose_name='Circuits' verbose_name='Circuits'
) )
tags = TagColumn(
url_name='circuits:provider_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Provider model = Provider
fields = ('pk', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count') fields = (
'pk', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count', 'tags',
)
default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count') default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
@ -45,7 +50,9 @@ class ProviderTable(BaseTable):
class CircuitTypeTable(BaseTable): class CircuitTypeTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
circuit_count = tables.Column(verbose_name='Circuits') circuit_count = tables.Column(
verbose_name='Circuits'
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=CIRCUITTYPE_ACTIONS, template_code=CIRCUITTYPE_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}}, attrs={'td': {'class': 'text-right noprint'}},
@ -64,21 +71,33 @@ class CircuitTypeTable(BaseTable):
class CircuitTable(BaseTable): class CircuitTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
cid = tables.LinkColumn(verbose_name='ID') cid = tables.LinkColumn(
provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')]) verbose_name='ID'
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status') )
tenant = tables.TemplateColumn(template_code=COL_TENANT) provider = tables.LinkColumn(
viewname='circuits:provider',
args=[Accessor('provider.slug')]
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
a_side = tables.Column( a_side = tables.Column(
verbose_name='A Side' verbose_name='A Side'
) )
z_side = tables.Column( z_side = tables.Column(
verbose_name='Z Side' verbose_name='Z Side'
) )
tags = TagColumn(
url_name='circuits:circuit_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Circuit model = Circuit
fields = ( fields = (
'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'install_date', 'commit_rate', 'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'install_date', 'commit_rate',
'description', 'description', 'tags',
) )
default_columns = ('pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'description') default_columns = ('pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'description')

View File

@ -2,7 +2,7 @@ import django_tables2 as tables
from django_tables2.utils import Accessor from django_tables2.utils import Accessor
from tenancy.tables import COL_TENANT from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn from utilities.tables import BaseTable, BooleanColumn, ColorColumn, TagColumn, ToggleColumn
from .models import ( from .models import (
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate, DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
@ -242,13 +242,16 @@ class SiteTable(BaseTable):
tenant = tables.TemplateColumn( tenant = tables.TemplateColumn(
template_code=COL_TENANT template_code=COL_TENANT
) )
tags = TagColumn(
url_name='dcim:site_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Site model = Site
fields = ( fields = (
'pk', 'name', 'slug', 'status', 'facility', 'region', 'tenant', 'asn', 'time_zone', 'description', 'pk', 'name', 'slug', 'status', 'facility', 'region', 'tenant', 'asn', 'time_zone', 'description',
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
'contact_email', 'contact_email', 'tags',
) )
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'tenant', 'asn', 'description') default_columns = ('pk', 'name', 'status', 'facility', 'region', 'tenant', 'asn', 'description')
@ -354,11 +357,14 @@ class RackDetailTable(RackTable):
orderable=False, orderable=False,
verbose_name='Power' verbose_name='Power'
) )
tags = TagColumn(
url_name='dcim:rack_list'
)
class Meta(RackTable.Meta): class Meta(RackTable.Meta):
fields = ( fields = (
'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type', 'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
'width', 'u_height', 'device_count', 'get_utilization', 'get_power_utilization', 'width', 'u_height', 'device_count', 'get_utilization', 'get_power_utilization', 'tags',
) )
default_columns = ( 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',
@ -450,17 +456,22 @@ class DeviceTypeTable(BaseTable):
args=[Accessor('pk')], args=[Accessor('pk')],
verbose_name='Device Type' verbose_name='Device Type'
) )
is_full_depth = BooleanColumn(verbose_name='Full Depth') is_full_depth = BooleanColumn(
verbose_name='Full Depth'
)
instance_count = tables.TemplateColumn( instance_count = tables.TemplateColumn(
template_code=DEVICETYPE_INSTANCES_TEMPLATE, template_code=DEVICETYPE_INSTANCES_TEMPLATE,
verbose_name='Instances' verbose_name='Instances'
) )
tags = TagColumn(
url_name='dcim:devicetype_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = DeviceType model = DeviceType
fields = ( fields = (
'pk', 'model', 'manufacturer', 'slug', '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', 'tags',
) )
default_columns = ( default_columns = (
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count', 'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',
@ -834,13 +845,16 @@ class DeviceTable(BaseTable):
vc_priority = tables.Column( vc_priority = tables.Column(
verbose_name='VC Priority' verbose_name='VC Priority'
) )
tags = TagColumn(
url_name='dcim:device_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Device model = Device
fields = ( fields = (
'pk', 'name', 'status', 'tenant', 'device_role', 'device_type', 'platform', 'serial', 'asset_tag', 'site', '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', 'rack', 'position', 'face', 'primary_ip', 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis',
'vc_position', 'vc_priority', 'vc_position', 'vc_priority', 'tags',
) )
default_columns = ( default_columns = (
'pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip', 'pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip',
@ -1206,10 +1220,13 @@ class VirtualChassisTable(BaseTable):
member_count = tables.Column( member_count = tables.Column(
verbose_name='Members' verbose_name='Members'
) )
tags = TagColumn(
url_name='dcim:virtualchassis_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VirtualChassis model = VirtualChassis
fields = ('pk', 'name', 'domain', 'member_count') fields = ('pk', 'name', 'domain', 'member_count', 'tags')
default_columns = ('pk', 'name', 'domain', 'member_count') default_columns = ('pk', 'name', 'domain', 'member_count')
@ -1262,12 +1279,15 @@ class PowerFeedTable(BaseTable):
available_power = tables.Column( available_power = tables.Column(
verbose_name='Available power (VA)' verbose_name='Available power (VA)'
) )
tags = TagColumn(
url_name='dcim:powerfeed_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = PowerFeed model = PowerFeed
fields = ( fields = (
'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase', 'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
'max_utilization', 'available_power', 'max_utilization', 'available_power', 'tags',
) )
default_columns = ( default_columns = (
'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase', 'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',

View File

@ -3,7 +3,7 @@ from django_tables2.utils import Accessor
from dcim.models import Interface from dcim.models import Interface
from tenancy.tables import COL_TENANT from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, BooleanColumn, ToggleColumn from utilities.tables import BaseTable, BooleanColumn, TagColumn, ToggleColumn
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
RIR_UTILIZATION = """ RIR_UTILIZATION = """
@ -199,10 +199,13 @@ class VRFTable(BaseTable):
enforce_unique = BooleanColumn( enforce_unique = BooleanColumn(
verbose_name='Unique' verbose_name='Unique'
) )
tags = TagColumn(
url_name='ipam:vrf_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VRF model = VRF
fields = ('pk', 'name', 'rd', 'tenant', 'enforce_unique', 'description') fields = ('pk', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'tags')
default_columns = ('pk', 'name', 'rd', 'tenant', 'description') default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
@ -300,9 +303,13 @@ class AggregateDetailTable(AggregateTable):
template_code=UTILIZATION_GRAPH, template_code=UTILIZATION_GRAPH,
orderable=False orderable=False
) )
tags = TagColumn(
url_name='ipam:aggregate_list'
)
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', 'tags')
default_columns = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description')
# #
@ -388,10 +395,14 @@ class PrefixDetailTable(PrefixTable):
tenant = tables.TemplateColumn( tenant = tables.TemplateColumn(
template_code=COL_TENANT template_code=COL_TENANT
) )
tags = TagColumn(
url_name='ipam:prefix_list'
)
class Meta(PrefixTable.Meta): class Meta(PrefixTable.Meta):
fields = ( fields = (
'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'is_pool', 'description', 'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'is_pool', 'description',
'tags',
) )
default_columns = ( default_columns = (
'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description', 'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
@ -446,11 +457,14 @@ class IPAddressDetailTable(IPAddressTable):
tenant = tables.TemplateColumn( tenant = tables.TemplateColumn(
template_code=COL_TENANT template_code=COL_TENANT
) )
tags = TagColumn(
url_name='ipam:ipaddress_list'
)
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', 'tags',
) )
default_columns = ( default_columns = (
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'dns_name', 'description', 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'dns_name', 'description',
@ -573,9 +587,13 @@ class VLANDetailTable(VLANTable):
tenant = tables.TemplateColumn( tenant = tables.TemplateColumn(
template_code=COL_TENANT template_code=COL_TENANT
) )
tags = TagColumn(
url_name='ipam:vlan_list'
)
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', 'tags')
default_columns = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
class VLANMemberTable(BaseTable): class VLANMemberTable(BaseTable):
@ -647,8 +665,11 @@ class ServiceTable(BaseTable):
viewname='ipam:service', viewname='ipam:service',
args=[Accessor('pk')] args=[Accessor('pk')]
) )
tags = TagColumn(
url_name='ipam:service_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Service model = Service
fields = ('pk', 'name', 'parent', 'protocol', 'port', 'ipaddresses', 'description') fields = ('pk', 'name', 'parent', 'protocol', 'port', 'ipaddresses', 'description', 'tags')
default_columns = ('pk', 'name', 'parent', 'protocol', 'port', 'description') default_columns = ('pk', 'name', 'parent', 'protocol', 'port', 'description')

View File

@ -1,6 +1,6 @@
import django_tables2 as tables import django_tables2 as tables
from utilities.tables import BaseTable, ToggleColumn from utilities.tables import BaseTable, TagColumn, ToggleColumn
from .models import SecretRole, Secret from .models import SecretRole, Secret
SECRETROLE_ACTIONS = """ SECRETROLE_ACTIONS = """
@ -42,8 +42,11 @@ class SecretRoleTable(BaseTable):
class SecretTable(BaseTable): class SecretTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
device = tables.LinkColumn() device = tables.LinkColumn()
tags = TagColumn(
url_name='secrets:secret_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Secret model = Secret
fields = ('pk', 'device', 'role', 'name', 'last_updated', 'hash') fields = ('pk', 'device', 'role', 'name', 'last_updated', 'hash', 'tags')
default_columns = ('pk', 'device', 'role', 'name', 'last_updated') default_columns = ('pk', 'device', 'role', 'name', 'last_updated')

View File

@ -1,5 +1,3 @@
{% load helpers %} {% load helpers %}
{% if url_name %}<a href="{% url url_name %}?tag={{ tag.slug }}">{% endif %} {% if url_name %}<a href="{% url url_name %}?tag={{ tag.slug }}">{% endif %}<span class="label label-default" style="color: {{ tag.color|fgcolor }}; background-color: #{{ tag.color }}">{{ tag }}</span>{% if url_name %}</a>{% endif %}
<span class="label label-default" style="color: {{ tag.color|fgcolor }}; background-color: #{{ tag.color }}">{{ tag }}</span>
{% if url_name %}</a>{% endif %}

View File

@ -1,6 +1,6 @@
import django_tables2 as tables import django_tables2 as tables
from utilities.tables import BaseTable, ToggleColumn from utilities.tables import BaseTable, TagColumn, ToggleColumn
from .models import Tenant, TenantGroup from .models import Tenant, TenantGroup
MPTT_LINK = """ MPTT_LINK = """
@ -63,8 +63,11 @@ class TenantGroupTable(BaseTable):
class TenantTable(BaseTable): class TenantTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
tags = TagColumn(
url_name='tenancy:tenant_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Tenant model = Tenant
fields = ('pk', 'name', 'slug', 'group', 'description') fields = ('pk', 'name', 'slug', 'group', 'description', 'tags')
default_columns = ('pk', 'name', 'group', 'description') default_columns = ('pk', 'name', 'group', 'description')

View File

@ -1,8 +1,9 @@
import django_tables2 as tables import django_tables2 as tables
from django.core.exceptions import FieldDoesNotExist from django.core.exceptions import FieldDoesNotExist
from django.db.models import ForeignKey from django.db.models import ForeignKey
from django_tables2.data import TableQuerysetData from django.db.models.fields.related import RelatedField
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django_tables2.data import TableQuerysetData
class BaseTable(tables.Table): class BaseTable(tables.Table):
@ -57,7 +58,7 @@ class BaseTable(tables.Table):
field_path = column.accessor.split('.') field_path = column.accessor.split('.')
try: try:
model_field = model._meta.get_field(field_path[0]) model_field = model._meta.get_field(field_path[0])
if isinstance(model_field, ForeignKey): if isinstance(model_field, RelatedField):
prefetch_fields.append('__'.join(field_path)) prefetch_fields.append('__'.join(field_path))
except FieldDoesNotExist: except FieldDoesNotExist:
pass pass
@ -121,3 +122,22 @@ class ColorColumn(tables.Column):
return mark_safe( return mark_safe(
'<span class="label color-block" style="background-color: #{}">&nbsp;</span>'.format(value) '<span class="label color-block" style="background-color: #{}">&nbsp;</span>'.format(value)
) )
class TagColumn(tables.TemplateColumn):
"""
Display a list of tags assigned to the object.
"""
template_code = """
{% for tag in value.all %}
{% include 'utilities/templatetags/tag.html' %}
{% empty %}
<span class="text-muted">&mdash;</span>
{% endfor %}
"""
def __init__(self, url_name=None):
super().__init__(
template_code=self.template_code,
extra_context={'url_name': url_name}
)

View File

@ -3,7 +3,7 @@ from django_tables2.utils import Accessor
from dcim.models import Interface from dcim.models import Interface
from tenancy.tables import COL_TENANT from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, ToggleColumn from utilities.tables import BaseTable, TagColumn, ToggleColumn
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
CLUSTERTYPE_ACTIONS = """ CLUSTERTYPE_ACTIONS = """
@ -108,10 +108,14 @@ class ClusterTable(BaseTable):
orderable=False, orderable=False,
verbose_name='VMs' verbose_name='VMs'
) )
tags = TagColumn(
url_name='virtualization:cluster_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Cluster model = Cluster
fields = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count') fields = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count', 'tags')
default_columns = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count')
# #
@ -156,12 +160,15 @@ class VirtualMachineDetailTable(VirtualMachineTable):
verbose_name='IP Address', verbose_name='IP Address',
template_code=VIRTUALMACHINE_PRIMARY_IP template_code=VIRTUALMACHINE_PRIMARY_IP
) )
tags = TagColumn(
url_name='virtualization:virtualmachine_list'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VirtualMachine model = VirtualMachine
fields = ( fields = (
'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'primary_ip4', 'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'primary_ip4',
'primary_ip6', 'primary_ip', 'primary_ip6', 'primary_ip', 'tags',
) )
default_columns = ( default_columns = (
'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip', 'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',