mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-21 03:27:21 -06:00
Merge branch 'global-search' into v2-develop
This commit is contained in:
commit
aefc6ff7b4
@ -1,7 +1,7 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
|
||||||
from utilities.tables import BaseTable, ToggleColumn
|
from utilities.tables import BaseTable, SearchTable, ToggleColumn
|
||||||
|
|
||||||
from .models import Circuit, CircuitType, Provider
|
from .models import Circuit, CircuitType, Provider
|
||||||
|
|
||||||
@ -19,9 +19,7 @@ CIRCUITTYPE_ACTIONS = """
|
|||||||
|
|
||||||
class ProviderTable(BaseTable):
|
class ProviderTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn('circuits:provider', args=[Accessor('slug')], verbose_name='Name')
|
name = tables.LinkColumn()
|
||||||
asn = tables.Column(verbose_name='ASN')
|
|
||||||
account = tables.Column(verbose_name='Account')
|
|
||||||
circuit_count = tables.Column(accessor=Accessor('count_circuits'), verbose_name='Circuits')
|
circuit_count = tables.Column(accessor=Accessor('count_circuits'), verbose_name='Circuits')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -29,17 +27,25 @@ class ProviderTable(BaseTable):
|
|||||||
fields = ('pk', 'name', 'asn', 'account', 'circuit_count')
|
fields = ('pk', 'name', 'asn', 'account', 'circuit_count')
|
||||||
|
|
||||||
|
|
||||||
|
class ProviderSearchTable(SearchTable):
|
||||||
|
name = tables.LinkColumn()
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Provider
|
||||||
|
fields = ('name', 'asn', 'account')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Circuit types
|
# Circuit types
|
||||||
#
|
#
|
||||||
|
|
||||||
class CircuitTypeTable(BaseTable):
|
class CircuitTypeTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn(verbose_name='Name')
|
name = tables.LinkColumn()
|
||||||
circuit_count = tables.Column(verbose_name='Circuits')
|
circuit_count = tables.Column(verbose_name='Circuits')
|
||||||
slug = tables.Column(verbose_name='Slug')
|
actions = tables.TemplateColumn(
|
||||||
actions = tables.TemplateColumn(template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}},
|
template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
|
||||||
verbose_name='')
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = CircuitType
|
model = CircuitType
|
||||||
@ -52,16 +58,28 @@ class CircuitTypeTable(BaseTable):
|
|||||||
|
|
||||||
class CircuitTable(BaseTable):
|
class CircuitTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
cid = tables.LinkColumn('circuits:circuit', args=[Accessor('pk')], verbose_name='ID')
|
cid = tables.LinkColumn(verbose_name='ID')
|
||||||
type = tables.Column(verbose_name='Type')
|
provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')])
|
||||||
provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')], verbose_name='Provider')
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
a_side = tables.LinkColumn(
|
||||||
a_side = tables.LinkColumn('dcim:site', accessor=Accessor('termination_a.site'), orderable=False,
|
'dcim:site', accessor=Accessor('termination_a.site'), orderable=False,
|
||||||
args=[Accessor('termination_a.site.slug')])
|
args=[Accessor('termination_a.site.slug')]
|
||||||
z_side = tables.LinkColumn('dcim:site', accessor=Accessor('termination_z.site'), orderable=False,
|
)
|
||||||
args=[Accessor('termination_z.site.slug')])
|
z_side = tables.LinkColumn(
|
||||||
description = tables.Column(verbose_name='Description')
|
'dcim:site', accessor=Accessor('termination_z.site'), orderable=False,
|
||||||
|
args=[Accessor('termination_z.site.slug')]
|
||||||
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Circuit
|
model = Circuit
|
||||||
fields = ('pk', 'cid', 'type', 'provider', 'tenant', 'a_side', 'z_side', 'description')
|
fields = ('pk', 'cid', 'type', 'provider', 'tenant', 'a_side', 'z_side', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class CircuitSearchTable(SearchTable):
|
||||||
|
cid = tables.LinkColumn(verbose_name='ID')
|
||||||
|
provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')])
|
||||||
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Circuit
|
||||||
|
fields = ('cid', 'type', 'provider', 'tenant', 'description')
|
||||||
|
@ -8,9 +8,9 @@ from tenancy.models import Tenant
|
|||||||
from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter
|
from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter
|
||||||
from .models import (
|
from .models import (
|
||||||
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||||
DeviceBayTemplate, DeviceRole, DeviceType, IFACE_FF_LAG, Interface, InterfaceConnection, InterfaceTemplate,
|
DeviceBayTemplate, DeviceRole, DeviceType, IFACE_FF_LAG, Interface, InterfaceTemplate, Manufacturer, InventoryItem,
|
||||||
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup,
|
Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation,
|
||||||
RackReservation, RackRole, Region, Site, VIRTUAL_IFACE_TYPES,
|
RackRole, Region, Site, VIRTUAL_IFACE_TYPES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
|
||||||
from utilities.tables import BaseTable, ToggleColumn
|
from utilities.tables import BaseTable, SearchTable, ToggleColumn
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType,
|
ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType,
|
||||||
@ -136,11 +136,9 @@ class RegionTable(BaseTable):
|
|||||||
|
|
||||||
class SiteTable(BaseTable):
|
class SiteTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn('dcim:site', args=[Accessor('slug')], verbose_name='Name')
|
name = tables.LinkColumn()
|
||||||
facility = tables.Column(verbose_name='Facility')
|
region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
|
||||||
region = tables.TemplateColumn(template_code=SITE_REGION_LINK, verbose_name='Region')
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
|
||||||
asn = tables.Column(verbose_name='ASN')
|
|
||||||
rack_count = tables.Column(accessor=Accessor('count_racks'), orderable=False, verbose_name='Racks')
|
rack_count = tables.Column(accessor=Accessor('count_racks'), orderable=False, verbose_name='Racks')
|
||||||
device_count = tables.Column(accessor=Accessor('count_devices'), orderable=False, verbose_name='Devices')
|
device_count = tables.Column(accessor=Accessor('count_devices'), orderable=False, verbose_name='Devices')
|
||||||
prefix_count = tables.Column(accessor=Accessor('count_prefixes'), orderable=False, verbose_name='Prefixes')
|
prefix_count = tables.Column(accessor=Accessor('count_prefixes'), orderable=False, verbose_name='Prefixes')
|
||||||
@ -155,6 +153,16 @@ class SiteTable(BaseTable):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SiteSearchTable(SearchTable):
|
||||||
|
name = tables.LinkColumn()
|
||||||
|
region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
|
||||||
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Site
|
||||||
|
fields = ('name', 'facility', 'region', 'tenant', 'asn')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Rack groups
|
# Rack groups
|
||||||
#
|
#
|
||||||
@ -197,20 +205,33 @@ class RackRoleTable(BaseTable):
|
|||||||
|
|
||||||
class RackTable(BaseTable):
|
class RackTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn('dcim:rack', args=[Accessor('pk')], verbose_name='Name')
|
name = tables.LinkColumn()
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
||||||
facility_id = tables.Column(verbose_name='Facility ID')
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
role = tables.TemplateColumn(RACK_ROLE)
|
||||||
role = tables.TemplateColumn(RACK_ROLE, verbose_name='Role')
|
|
||||||
u_height = tables.TemplateColumn("{{ record.u_height }}U", verbose_name='Height')
|
u_height = tables.TemplateColumn("{{ record.u_height }}U", verbose_name='Height')
|
||||||
devices = tables.Column(accessor=Accessor('device_count'), verbose_name='Devices')
|
devices = tables.Column(accessor=Accessor('device_count'))
|
||||||
get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
|
get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Rack
|
model = Rack
|
||||||
fields = ('pk', 'name', 'site', 'group', 'facility_id', 'tenant', 'role', 'u_height', 'devices',
|
fields = (
|
||||||
'get_utilization')
|
'pk', 'name', 'site', 'group', 'facility_id', 'tenant', 'role', 'u_height', 'devices', 'get_utilization'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RackSearchTable(SearchTable):
|
||||||
|
name = tables.LinkColumn()
|
||||||
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
|
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
||||||
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
|
role = tables.TemplateColumn(RACK_ROLE)
|
||||||
|
u_height = tables.TemplateColumn("{{ record.u_height }}U", verbose_name='Height')
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Rack
|
||||||
|
fields = ('name', 'site', 'group', 'facility_id', 'tenant', 'role', 'u_height')
|
||||||
|
|
||||||
|
|
||||||
class RackImportTable(BaseTable):
|
class RackImportTable(BaseTable):
|
||||||
@ -249,9 +270,7 @@ class ManufacturerTable(BaseTable):
|
|||||||
|
|
||||||
class DeviceTypeTable(BaseTable):
|
class DeviceTypeTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
manufacturer = tables.Column(verbose_name='Manufacturer')
|
|
||||||
model = tables.LinkColumn('dcim:devicetype', args=[Accessor('pk')], verbose_name='Device Type')
|
model = tables.LinkColumn('dcim:devicetype', args=[Accessor('pk')], verbose_name='Device Type')
|
||||||
part_number = tables.Column(verbose_name='Part Number')
|
|
||||||
is_full_depth = tables.BooleanColumn(verbose_name='Full Depth')
|
is_full_depth = tables.BooleanColumn(verbose_name='Full Depth')
|
||||||
is_console_server = tables.BooleanColumn(verbose_name='CS')
|
is_console_server = tables.BooleanColumn(verbose_name='CS')
|
||||||
is_pdu = tables.BooleanColumn(verbose_name='PDU')
|
is_pdu = tables.BooleanColumn(verbose_name='PDU')
|
||||||
@ -267,6 +286,22 @@ class DeviceTypeTable(BaseTable):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceTypeSearchTable(SearchTable):
|
||||||
|
model = tables.LinkColumn('dcim:devicetype', args=[Accessor('pk')], verbose_name='Device Type')
|
||||||
|
is_full_depth = tables.BooleanColumn(verbose_name='Full Depth')
|
||||||
|
is_console_server = tables.BooleanColumn(verbose_name='CS')
|
||||||
|
is_pdu = tables.BooleanColumn(verbose_name='PDU')
|
||||||
|
is_network_device = tables.BooleanColumn(verbose_name='Net')
|
||||||
|
subdevice_role = tables.TemplateColumn(SUBDEVICE_ROLE_TEMPLATE, verbose_name='Subdevice Role')
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = DeviceType
|
||||||
|
fields = (
|
||||||
|
'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
|
||||||
|
'is_network_device', 'subdevice_role',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device type components
|
# Device type components
|
||||||
#
|
#
|
||||||
@ -373,22 +408,42 @@ class PlatformTable(BaseTable):
|
|||||||
|
|
||||||
class DeviceTable(BaseTable):
|
class DeviceTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.TemplateColumn(template_code=DEVICE_LINK)
|
||||||
status = tables.TemplateColumn(template_code=STATUS_ICON, verbose_name='')
|
status = tables.TemplateColumn(template_code=STATUS_ICON, verbose_name='')
|
||||||
name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name')
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
|
||||||
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')], verbose_name='Rack')
|
|
||||||
device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role')
|
device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role')
|
||||||
device_type = tables.LinkColumn('dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type',
|
device_type = tables.LinkColumn(
|
||||||
text=lambda record: record.device_type.full_name)
|
'dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type',
|
||||||
primary_ip = tables.TemplateColumn(orderable=False, verbose_name='IP Address',
|
text=lambda record: record.device_type.full_name
|
||||||
template_code=DEVICE_PRIMARY_IP)
|
)
|
||||||
|
primary_ip = tables.TemplateColumn(
|
||||||
|
orderable=False, verbose_name='IP Address', template_code=DEVICE_PRIMARY_IP
|
||||||
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Device
|
model = Device
|
||||||
fields = ('pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip')
|
fields = ('pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip')
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceSearchTable(SearchTable):
|
||||||
|
name = tables.TemplateColumn(template_code=DEVICE_LINK)
|
||||||
|
status = tables.TemplateColumn(template_code=STATUS_ICON, verbose_name='')
|
||||||
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
|
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
|
||||||
|
device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role')
|
||||||
|
device_type = tables.LinkColumn(
|
||||||
|
'dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type',
|
||||||
|
text=lambda record: record.device_type.full_name
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Device
|
||||||
|
fields = ('name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type')
|
||||||
|
|
||||||
|
|
||||||
class DeviceImportTable(BaseTable):
|
class DeviceImportTable(BaseTable):
|
||||||
name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name')
|
name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name')
|
||||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
|
||||||
from utilities.tables import BaseTable, ToggleColumn
|
from utilities.tables import BaseTable, SearchTable, ToggleColumn
|
||||||
|
|
||||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VLANGroup, VRF
|
from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VLANGroup, VRF
|
||||||
|
|
||||||
@ -133,16 +133,25 @@ TENANT_LINK = """
|
|||||||
|
|
||||||
class VRFTable(BaseTable):
|
class VRFTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn('ipam:vrf', args=[Accessor('pk')], verbose_name='Name')
|
name = tables.LinkColumn()
|
||||||
rd = tables.Column(verbose_name='RD')
|
rd = tables.Column(verbose_name='RD')
|
||||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
description = tables.Column(verbose_name='Description')
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = VRF
|
model = VRF
|
||||||
fields = ('pk', 'name', 'rd', 'tenant', 'description')
|
fields = ('pk', 'name', 'rd', 'tenant', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class VRFSearchTable(SearchTable):
|
||||||
|
name = tables.LinkColumn()
|
||||||
|
rd = tables.Column(verbose_name='RD')
|
||||||
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = VRF
|
||||||
|
fields = ('name', 'rd', 'tenant', 'description')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# RIRs
|
# RIRs
|
||||||
#
|
#
|
||||||
@ -177,18 +186,25 @@ class RIRTable(BaseTable):
|
|||||||
|
|
||||||
class AggregateTable(BaseTable):
|
class AggregateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
prefix = tables.LinkColumn('ipam:aggregate', args=[Accessor('pk')], verbose_name='Aggregate')
|
prefix = tables.LinkColumn(verbose_name='Aggregate')
|
||||||
rir = tables.Column(verbose_name='RIR')
|
|
||||||
child_count = tables.Column(verbose_name='Prefixes')
|
child_count = tables.Column(verbose_name='Prefixes')
|
||||||
get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
|
get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
|
||||||
date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added')
|
date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added')
|
||||||
description = tables.Column(verbose_name='Description')
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Aggregate
|
model = Aggregate
|
||||||
fields = ('pk', 'prefix', 'rir', 'child_count', 'get_utilization', 'date_added', 'description')
|
fields = ('pk', 'prefix', 'rir', 'child_count', 'get_utilization', 'date_added', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class AggregateSearchTable(SearchTable):
|
||||||
|
prefix = tables.LinkColumn(verbose_name='Aggregate')
|
||||||
|
date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added')
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Aggregate
|
||||||
|
fields = ('prefix', 'rir', 'date_added', 'description')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Roles
|
# Roles
|
||||||
#
|
#
|
||||||
@ -212,14 +228,13 @@ class RoleTable(BaseTable):
|
|||||||
|
|
||||||
class PrefixTable(BaseTable):
|
class PrefixTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
|
prefix = tables.TemplateColumn(PREFIX_LINK, attrs={'th': {'style': 'padding-left: 17px'}})
|
||||||
prefix = tables.TemplateColumn(PREFIX_LINK, verbose_name='Prefix', attrs={'th': {'style': 'padding-left: 17px'}})
|
status = tables.TemplateColumn(STATUS_LABEL)
|
||||||
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
|
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
|
||||||
tenant = tables.TemplateColumn(TENANT_LINK, verbose_name='Tenant')
|
tenant = tables.TemplateColumn(TENANT_LINK)
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN')
|
vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN')
|
||||||
role = tables.TemplateColumn(PREFIX_ROLE_LINK, verbose_name='Role')
|
role = tables.TemplateColumn(PREFIX_ROLE_LINK)
|
||||||
description = tables.Column(verbose_name='Description')
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Prefix
|
model = Prefix
|
||||||
@ -230,12 +245,11 @@ class PrefixTable(BaseTable):
|
|||||||
|
|
||||||
|
|
||||||
class PrefixBriefTable(BaseTable):
|
class PrefixBriefTable(BaseTable):
|
||||||
prefix = tables.TemplateColumn(PREFIX_LINK_BRIEF, verbose_name='Prefix')
|
prefix = tables.TemplateColumn(PREFIX_LINK_BRIEF)
|
||||||
vrf = tables.LinkColumn('ipam:vrf', args=[Accessor('vrf.pk')], default='Global', verbose_name='VRF')
|
vrf = tables.LinkColumn('ipam:vrf', args=[Accessor('vrf.pk')], default='Global')
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(STATUS_LABEL)
|
||||||
vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN')
|
vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')])
|
||||||
role = tables.Column(verbose_name='Role')
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Prefix
|
model = Prefix
|
||||||
@ -243,6 +257,20 @@ class PrefixBriefTable(BaseTable):
|
|||||||
orderable = False
|
orderable = False
|
||||||
|
|
||||||
|
|
||||||
|
class PrefixSearchTable(SearchTable):
|
||||||
|
prefix = tables.TemplateColumn(PREFIX_LINK, attrs={'th': {'style': 'padding-left: 17px'}})
|
||||||
|
status = tables.TemplateColumn(STATUS_LABEL)
|
||||||
|
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
|
||||||
|
tenant = tables.TemplateColumn(TENANT_LINK)
|
||||||
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
|
vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN')
|
||||||
|
role = tables.TemplateColumn(PREFIX_ROLE_LINK)
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Prefix
|
||||||
|
fields = ('prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'description')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# IPAddresses
|
# IPAddresses
|
||||||
#
|
#
|
||||||
@ -250,13 +278,11 @@ class PrefixBriefTable(BaseTable):
|
|||||||
class IPAddressTable(BaseTable):
|
class IPAddressTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address')
|
address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address')
|
||||||
status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(STATUS_LABEL)
|
||||||
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
|
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
|
||||||
tenant = tables.TemplateColumn(TENANT_LINK, verbose_name='Tenant')
|
tenant = tables.TemplateColumn(TENANT_LINK)
|
||||||
device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False,
|
device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False)
|
||||||
verbose_name='Device')
|
interface = tables.Column(orderable=False)
|
||||||
interface = tables.Column(orderable=False, verbose_name='Interface')
|
|
||||||
description = tables.Column(verbose_name='Description')
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
@ -268,17 +294,30 @@ class IPAddressTable(BaseTable):
|
|||||||
|
|
||||||
class IPAddressBriefTable(BaseTable):
|
class IPAddressBriefTable(BaseTable):
|
||||||
address = tables.LinkColumn('ipam:ipaddress', args=[Accessor('pk')], verbose_name='IP Address')
|
address = tables.LinkColumn('ipam:ipaddress', args=[Accessor('pk')], verbose_name='IP Address')
|
||||||
device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False,
|
device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False)
|
||||||
verbose_name='Device')
|
interface = tables.Column(orderable=False)
|
||||||
interface = tables.Column(orderable=False, verbose_name='Interface')
|
nat_inside = tables.LinkColumn(
|
||||||
nat_inside = tables.LinkColumn('ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False,
|
'ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False, verbose_name='NAT (Inside)'
|
||||||
verbose_name='NAT (Inside)')
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
fields = ('address', 'device', 'interface', 'nat_inside')
|
fields = ('address', 'device', 'interface', 'nat_inside')
|
||||||
|
|
||||||
|
|
||||||
|
class IPAddressSearchTable(SearchTable):
|
||||||
|
address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address')
|
||||||
|
status = tables.TemplateColumn(STATUS_LABEL)
|
||||||
|
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
|
||||||
|
tenant = tables.TemplateColumn(TENANT_LINK)
|
||||||
|
device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False)
|
||||||
|
interface = tables.Column(orderable=False)
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = IPAddress
|
||||||
|
fields = ('address', 'status', 'vrf', 'tenant', 'device', 'interface', 'description')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# VLAN groups
|
# VLAN groups
|
||||||
#
|
#
|
||||||
@ -304,15 +343,26 @@ class VLANGroupTable(BaseTable):
|
|||||||
class VLANTable(BaseTable):
|
class VLANTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
|
vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
||||||
name = tables.Column(verbose_name='Name')
|
|
||||||
prefixes = tables.TemplateColumn(VLAN_PREFIXES, orderable=False, verbose_name='Prefixes')
|
prefixes = tables.TemplateColumn(VLAN_PREFIXES, orderable=False, verbose_name='Prefixes')
|
||||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(STATUS_LABEL)
|
||||||
role = tables.TemplateColumn(VLAN_ROLE_LINK, verbose_name='Role')
|
role = tables.TemplateColumn(VLAN_ROLE_LINK)
|
||||||
description = tables.Column(verbose_name='Description')
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = VLAN
|
model = VLAN
|
||||||
fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
|
fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class VLANSearchTable(SearchTable):
|
||||||
|
vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
|
||||||
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
|
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
||||||
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||||
|
status = tables.TemplateColumn(STATUS_LABEL)
|
||||||
|
role = tables.TemplateColumn(VLAN_ROLE_LINK)
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = VLAN
|
||||||
|
fields = ('vid', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
|
||||||
|
40
netbox/netbox/forms.py
Normal file
40
netbox/netbox/forms.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
from utilities.forms import BootstrapMixin
|
||||||
|
|
||||||
|
|
||||||
|
OBJ_TYPE_CHOICES = (
|
||||||
|
('', 'All Objects'),
|
||||||
|
('Circuits', (
|
||||||
|
('provider', 'Providers'),
|
||||||
|
('circuit', 'Circuits'),
|
||||||
|
)),
|
||||||
|
('DCIM', (
|
||||||
|
('site', 'Sites'),
|
||||||
|
('rack', 'Racks'),
|
||||||
|
('devicetype', 'Device types'),
|
||||||
|
('device', 'Devices'),
|
||||||
|
)),
|
||||||
|
('IPAM', (
|
||||||
|
('vrf', 'VRFs'),
|
||||||
|
('aggregate', 'Aggregates'),
|
||||||
|
('prefix', 'Prefixes'),
|
||||||
|
('ipaddress', 'IP addresses'),
|
||||||
|
('vlan', 'VLANs'),
|
||||||
|
)),
|
||||||
|
('Secrets', (
|
||||||
|
('secret', 'Secrets'),
|
||||||
|
)),
|
||||||
|
('Tenancy', (
|
||||||
|
('tenant', 'Tenants'),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SearchForm(BootstrapMixin, forms.Form):
|
||||||
|
q = forms.CharField(
|
||||||
|
label='Query', widget=forms.TextInput(attrs={'style': 'width: 350px'})
|
||||||
|
)
|
||||||
|
obj_type = forms.ChoiceField(
|
||||||
|
choices=OBJ_TYPE_CHOICES, required=False, label='Type'
|
||||||
|
)
|
@ -2,7 +2,7 @@ from django.conf import settings
|
|||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from netbox.views import APIRootView, home, handle_500, trigger_500
|
from netbox.views import APIRootView, home, handle_500, SearchView, trigger_500
|
||||||
from users.views import login, logout
|
from users.views import login, logout
|
||||||
|
|
||||||
|
|
||||||
@ -10,8 +10,9 @@ handler500 = handle_500
|
|||||||
|
|
||||||
_patterns = [
|
_patterns = [
|
||||||
|
|
||||||
# Default page
|
# Base views
|
||||||
url(r'^$', home, name='home'),
|
url(r'^$', home, name='home'),
|
||||||
|
url(r'^search/$', SearchView.as_view(), name='search'),
|
||||||
|
|
||||||
# Login/logout
|
# Login/logout
|
||||||
url(r'^login/$', login, name='login'),
|
url(r'^login/$', login, name='login'),
|
||||||
|
@ -1,18 +1,117 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from rest_framework.permissions import IsAuthenticated
|
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.reverse import reverse
|
from rest_framework.reverse import reverse
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.views.generic import View
|
||||||
|
|
||||||
from circuits.models import Provider, Circuit
|
from circuits.filters import CircuitFilter, ProviderFilter
|
||||||
from dcim.models import Site, Rack, Device, ConsolePort, PowerPort, InterfaceConnection
|
from circuits.models import Circuit, Provider
|
||||||
|
from circuits.tables import CircuitSearchTable, ProviderSearchTable
|
||||||
|
from dcim.filters import DeviceFilter, DeviceTypeFilter, RackFilter, SiteFilter
|
||||||
|
from dcim.models import ConsolePort, Device, DeviceType, InterfaceConnection, PowerPort, Rack, Site
|
||||||
|
from dcim.tables import DeviceSearchTable, DeviceTypeSearchTable, RackSearchTable, SiteSearchTable
|
||||||
from extras.models import UserAction
|
from extras.models import UserAction
|
||||||
from ipam.models import Aggregate, Prefix, IPAddress, VLAN, VRF
|
from ipam.filters import AggregateFilter, IPAddressFilter, PrefixFilter, VLANFilter, VRFFilter
|
||||||
|
from ipam.models import Aggregate, IPAddress, Prefix, VLAN, VRF
|
||||||
|
from ipam.tables import AggregateSearchTable, IPAddressSearchTable, PrefixSearchTable, VLANSearchTable, VRFSearchTable
|
||||||
|
from secrets.filters import SecretFilter
|
||||||
from secrets.models import Secret
|
from secrets.models import Secret
|
||||||
|
from secrets.tables import SecretSearchTable
|
||||||
|
from tenancy.filters import TenantFilter
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
from tenancy.tables import TenantSearchTable
|
||||||
|
from .forms import SearchForm
|
||||||
|
|
||||||
|
|
||||||
|
SEARCH_MAX_RESULTS = 15
|
||||||
|
SEARCH_TYPES = {
|
||||||
|
# Circuits
|
||||||
|
'provider': {
|
||||||
|
'queryset': Provider.objects.all(),
|
||||||
|
'filter': ProviderFilter,
|
||||||
|
'table': ProviderSearchTable,
|
||||||
|
'url': 'circuits:provider_list',
|
||||||
|
},
|
||||||
|
'circuit': {
|
||||||
|
'queryset': Circuit.objects.select_related('type', 'provider', 'tenant'),
|
||||||
|
'filter': CircuitFilter,
|
||||||
|
'table': CircuitSearchTable,
|
||||||
|
'url': 'circuits:circuit_list',
|
||||||
|
},
|
||||||
|
# DCIM
|
||||||
|
'site': {
|
||||||
|
'queryset': Site.objects.select_related('region', 'tenant'),
|
||||||
|
'filter': SiteFilter,
|
||||||
|
'table': SiteSearchTable,
|
||||||
|
'url': 'dcim:site_list',
|
||||||
|
},
|
||||||
|
'rack': {
|
||||||
|
'queryset': Rack.objects.select_related('site', 'group', 'tenant', 'role'),
|
||||||
|
'filter': RackFilter,
|
||||||
|
'table': RackSearchTable,
|
||||||
|
'url': 'dcim:rack_list',
|
||||||
|
},
|
||||||
|
'devicetype': {
|
||||||
|
'queryset': DeviceType.objects.select_related('manufacturer'),
|
||||||
|
'filter': DeviceTypeFilter,
|
||||||
|
'table': DeviceTypeSearchTable,
|
||||||
|
'url': 'dcim:devicetype_list',
|
||||||
|
},
|
||||||
|
'device': {
|
||||||
|
'queryset': Device.objects.select_related('device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack'),
|
||||||
|
'filter': DeviceFilter,
|
||||||
|
'table': DeviceSearchTable,
|
||||||
|
'url': 'dcim:device_list',
|
||||||
|
},
|
||||||
|
# IPAM
|
||||||
|
'vrf': {
|
||||||
|
'queryset': VRF.objects.select_related('tenant'),
|
||||||
|
'filter': VRFFilter,
|
||||||
|
'table': VRFSearchTable,
|
||||||
|
'url': 'ipam:vrf_list',
|
||||||
|
},
|
||||||
|
'aggregate': {
|
||||||
|
'queryset': Aggregate.objects.select_related('rir'),
|
||||||
|
'filter': AggregateFilter,
|
||||||
|
'table': AggregateSearchTable,
|
||||||
|
'url': 'ipam:aggregate_list',
|
||||||
|
},
|
||||||
|
'prefix': {
|
||||||
|
'queryset': Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'),
|
||||||
|
'filter': PrefixFilter,
|
||||||
|
'table': PrefixSearchTable,
|
||||||
|
'url': 'ipam:prefix_list',
|
||||||
|
},
|
||||||
|
'ipaddress': {
|
||||||
|
'queryset': IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device'),
|
||||||
|
'filter': IPAddressFilter,
|
||||||
|
'table': IPAddressSearchTable,
|
||||||
|
'url': 'ipam:ipaddress_list',
|
||||||
|
},
|
||||||
|
'vlan': {
|
||||||
|
'queryset': VLAN.objects.select_related('site', 'group', 'tenant', 'role'),
|
||||||
|
'filter': VLANFilter,
|
||||||
|
'table': VLANSearchTable,
|
||||||
|
'url': 'ipam:vlan_list',
|
||||||
|
},
|
||||||
|
# Secrets
|
||||||
|
'secret': {
|
||||||
|
'queryset': Secret.objects.select_related('role', 'device'),
|
||||||
|
'filter': SecretFilter,
|
||||||
|
'table': SecretSearchTable,
|
||||||
|
'url': 'secrets:secret_list',
|
||||||
|
},
|
||||||
|
# Tenancy
|
||||||
|
'tenant': {
|
||||||
|
'queryset': Tenant.objects.select_related('group'),
|
||||||
|
'filter': TenantFilter,
|
||||||
|
'table': TenantSearchTable,
|
||||||
|
'url': 'tenancy:tenant_list',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def home(request):
|
def home(request):
|
||||||
@ -47,11 +146,59 @@ def home(request):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'home.html', {
|
return render(request, 'home.html', {
|
||||||
|
'search_form': SearchForm(),
|
||||||
'stats': stats,
|
'stats': stats,
|
||||||
'recent_activity': UserAction.objects.select_related('user')[:50]
|
'recent_activity': UserAction.objects.select_related('user')[:50]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class SearchView(View):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
|
||||||
|
# No query
|
||||||
|
if 'q' not in request.GET:
|
||||||
|
return render(request, 'search.html', {
|
||||||
|
'form': SearchForm(),
|
||||||
|
})
|
||||||
|
|
||||||
|
form = SearchForm(request.GET)
|
||||||
|
results = []
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
|
||||||
|
# Searching for a single type of object
|
||||||
|
if form.cleaned_data['obj_type']:
|
||||||
|
obj_types = [form.cleaned_data['obj_type']]
|
||||||
|
# Searching all object types
|
||||||
|
else:
|
||||||
|
obj_types = SEARCH_TYPES.keys()
|
||||||
|
|
||||||
|
for obj_type in obj_types:
|
||||||
|
|
||||||
|
queryset = SEARCH_TYPES[obj_type]['queryset']
|
||||||
|
filter_cls = SEARCH_TYPES[obj_type]['filter']
|
||||||
|
table = SEARCH_TYPES[obj_type]['table']
|
||||||
|
url = SEARCH_TYPES[obj_type]['url']
|
||||||
|
|
||||||
|
# Construct the results table for this object type
|
||||||
|
filtered_queryset = filter_cls({'q': form.cleaned_data['q']}, queryset=queryset).qs
|
||||||
|
table = table(filtered_queryset)
|
||||||
|
table.paginate(per_page=SEARCH_MAX_RESULTS)
|
||||||
|
|
||||||
|
if table.page:
|
||||||
|
results.append({
|
||||||
|
'name': queryset.model._meta.verbose_name_plural,
|
||||||
|
'table': table,
|
||||||
|
'url': '{}?q={}'.format(reverse(url), form.cleaned_data['q'])
|
||||||
|
})
|
||||||
|
|
||||||
|
return render(request, 'search.html', {
|
||||||
|
'form': form,
|
||||||
|
'results': results,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class APIRootView(APIView):
|
class APIRootView(APIView):
|
||||||
_ignore_model_permissions = True
|
_ignore_model_permissions = True
|
||||||
exclude_from_schema = True
|
exclude_from_schema = True
|
||||||
|
@ -92,6 +92,9 @@ tfoot td {
|
|||||||
table.attr-table td:nth-child(1) {
|
table.attr-table td:nth-child(1) {
|
||||||
width: 25%;
|
width: 25%;
|
||||||
}
|
}
|
||||||
|
.table-headings th {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
/* Paginator */
|
/* Paginator */
|
||||||
div.paginator {
|
div.paginator {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
|
||||||
from utilities.tables import BaseTable, ToggleColumn
|
from utilities.tables import BaseTable, SearchTable, ToggleColumn
|
||||||
|
|
||||||
from .models import SecretRole, Secret
|
from .models import SecretRole, Secret
|
||||||
|
|
||||||
@ -36,11 +36,15 @@ class SecretRoleTable(BaseTable):
|
|||||||
|
|
||||||
class SecretTable(BaseTable):
|
class SecretTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
device = tables.LinkColumn('secrets:secret', args=[Accessor('pk')], verbose_name='Device')
|
device = tables.LinkColumn()
|
||||||
role = tables.Column(verbose_name='Role')
|
|
||||||
name = tables.Column(verbose_name='Name')
|
|
||||||
last_updated = tables.DateTimeColumn(verbose_name='Last updated')
|
|
||||||
|
|
||||||
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')
|
||||||
|
|
||||||
|
|
||||||
|
class SecretSearchTable(SearchTable):
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Secret
|
||||||
|
fields = ('device', 'role', 'name', 'last_updated')
|
||||||
|
@ -48,7 +48,7 @@ class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
class SecretListView(ObjectListView):
|
class SecretListView(ObjectListView):
|
||||||
queryset = Secret.objects.select_related('role').prefetch_related('device')
|
queryset = Secret.objects.select_related('role', 'device')
|
||||||
filter = filters.SecretFilter
|
filter = filters.SecretFilter
|
||||||
filter_form = forms.SecretFilterForm
|
filter_form = forms.SecretFilterForm
|
||||||
table = tables.SecretTable
|
table = tables.SecretTable
|
||||||
|
@ -2,64 +2,7 @@
|
|||||||
{% load render_table from django_tables2 %}
|
{% load render_table from django_tables2 %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row home-search" style="padding: 15px 0px 20px">
|
{% include 'search_form.html' %}
|
||||||
<div class="col-sm-6 col-md-3">
|
|
||||||
<form action="{% url 'dcim:device_list' %}" method="get">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" name="q" placeholder="Search devices" class="form-control" />
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<span class="fa fa-search" aria-hidden="true"></span>
|
|
||||||
Devices
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<p></p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 col-md-3">
|
|
||||||
<form action="{% url 'ipam:prefix_list' %}" method="get">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" name="q" placeholder="Search prefixes" class="form-control" />
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<span class="fa fa-search" aria-hidden="true"></span>
|
|
||||||
Prefixes
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<p></p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 col-md-3">
|
|
||||||
<form action="{% url 'ipam:ipaddress_list' %}" method="get">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" name="q" placeholder="Search IPs" class="form-control" />
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<span class="fa fa-search" aria-hidden="true"></span>
|
|
||||||
IPs
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<p></p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 col-md-3">
|
|
||||||
<form action="{% url 'circuits:circuit_list' %}" method="get">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" name="q" placeholder="Search circuits" class="form-control" />
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<span class="fa fa-search" aria-hidden="true"></span>
|
|
||||||
Circuits
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<p></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-4">
|
<div class="col-sm-6 col-md-4">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block pagination %}
|
{% block pagination %}
|
||||||
{% include 'paginator.html' %}
|
{% if not hide_paginator %}
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
{% endif %}
|
||||||
{% endblock pagination %}
|
{% endblock pagination %}
|
||||||
|
|
||||||
|
65
netbox/templates/search.html
Normal file
65
netbox/templates/search.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{% extends '_base.html' %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load form_helpers %}
|
||||||
|
|
||||||
|
{% block title %}Search{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% if request.GET.q %}
|
||||||
|
{% include 'search_form.html' with search_form=form %}
|
||||||
|
{% if results %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10">
|
||||||
|
{% for obj_type in results %}
|
||||||
|
<h3 id="{{ obj_type.name|lower }}">{{ obj_type.name|bettertitle }}</h3>
|
||||||
|
{% include 'panel_table.html' with table=obj_type.table hide_paginator=True %}
|
||||||
|
{% if obj_type.table.page.has_next %}
|
||||||
|
<a href="{{ obj_type.url }}" class="btn btn-primary pull-right">
|
||||||
|
<span class="fa fa-arrow-right" aria-hidden="true"></span>
|
||||||
|
See all {{ obj_type.table.page.paginator.count }} results
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2" style="padding-top: 20px;">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Search Results</strong>
|
||||||
|
</div>
|
||||||
|
<div class="list-group">
|
||||||
|
{% for obj_type in results %}
|
||||||
|
<a href="#{{ obj_type.name|lower }}" class="list-group-item">
|
||||||
|
{{ obj_type.name|bettertitle }}
|
||||||
|
<span class="badge">{{ obj_type.table.page.paginator.count }}</span>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<h3 class="text-muted text-center">No results found</h3>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<div class="row" style="margin-top: 150px;">
|
||||||
|
<div class="col-sm-4 col-sm-offset-4">
|
||||||
|
<form action="{% url 'search' %}" method="get" class="form form-horizontal">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Search</strong>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{% render_form form %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer text-right">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<span class="fa fa-search" aria-hidden="true"></span> Search
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
9
netbox/templates/search_form.html
Normal file
9
netbox/templates/search_form.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<div class="row" style="padding-bottom: 20px">
|
||||||
|
<div class="col-md-12 text-center">
|
||||||
|
<form action="{% url 'search' %}" method="get" class="form-inline">
|
||||||
|
{{ search_form.q }}
|
||||||
|
{{ search_form.obj_type }}
|
||||||
|
<button type="submit" class="btn btn-primary">Search</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -4,5 +4,7 @@
|
|||||||
{# Extends the stock django_tables2 template to provide custom formatting of the pagination controls #}
|
{# Extends the stock django_tables2 template to provide custom formatting of the pagination controls #}
|
||||||
|
|
||||||
{% block pagination %}
|
{% block pagination %}
|
||||||
{% include 'paginator.html' %}
|
{% if not hide_paginator %}
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
{% endif %}
|
||||||
{% endblock pagination %}
|
{% endblock pagination %}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
|
||||||
from utilities.tables import BaseTable, ToggleColumn
|
from utilities.tables import BaseTable, SearchTable, ToggleColumn
|
||||||
|
|
||||||
from .models import Tenant, TenantGroup
|
from .models import Tenant, TenantGroup
|
||||||
|
|
||||||
@ -36,10 +36,15 @@ class TenantGroupTable(BaseTable):
|
|||||||
|
|
||||||
class TenantTable(BaseTable):
|
class TenantTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn('tenancy:tenant', args=[Accessor('slug')], verbose_name='Name')
|
name = tables.LinkColumn()
|
||||||
group = tables.Column(verbose_name='Group')
|
|
||||||
description = tables.Column(verbose_name='Description')
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Tenant
|
model = Tenant
|
||||||
fields = ('pk', 'name', 'group', 'description')
|
fields = ('pk', 'name', 'group', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class TenantSearchTable(SearchTable):
|
||||||
|
|
||||||
|
class Meta(SearchTable.Meta):
|
||||||
|
model = Tenant
|
||||||
|
fields = ('name', 'group', 'description')
|
||||||
|
@ -4,7 +4,9 @@ from django.utils.safestring import mark_safe
|
|||||||
|
|
||||||
|
|
||||||
class BaseTable(tables.Table):
|
class BaseTable(tables.Table):
|
||||||
|
"""
|
||||||
|
Default table for object lists
|
||||||
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(BaseTable, self).__init__(*args, **kwargs)
|
super(BaseTable, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@ -18,6 +20,17 @@ class BaseTable(tables.Table):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SearchTable(tables.Table):
|
||||||
|
"""
|
||||||
|
Default table for search results
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
attrs = {
|
||||||
|
'class': 'table table-hover table-headings',
|
||||||
|
}
|
||||||
|
orderable = False
|
||||||
|
|
||||||
|
|
||||||
class ToggleColumn(tables.CheckBoxColumn):
|
class ToggleColumn(tables.CheckBoxColumn):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -51,6 +51,13 @@ def startswith(value, arg):
|
|||||||
"""
|
"""
|
||||||
return str(value).startswith(arg)
|
return str(value).startswith(arg)
|
||||||
|
|
||||||
|
@register.filter()
|
||||||
|
def bettertitle(value):
|
||||||
|
"""
|
||||||
|
Alternative to the builtin title(); uppercases words without replacing letters that are already uppercase.
|
||||||
|
"""
|
||||||
|
return ' '.join([w[0].upper() + w[1:] for w in value.split()])
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Tags
|
# Tags
|
||||||
|
Loading…
Reference in New Issue
Block a user