devices.py 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081
  1. import django_tables2 as tables
  2. from django.utils.translation import gettext_lazy as _
  3. from django_tables2.utils import Accessor
  4. from dcim import models
  5. from netbox.tables import NetBoxTable, columns
  6. from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
  7. from .template_code import *
  8. __all__ = (
  9. 'BaseInterfaceTable',
  10. 'CableTerminationTable',
  11. 'ConsolePortTable',
  12. 'ConsoleServerPortTable',
  13. 'DeviceBayTable',
  14. 'DeviceConsolePortTable',
  15. 'DeviceConsoleServerPortTable',
  16. 'DeviceDeviceBayTable',
  17. 'DeviceFrontPortTable',
  18. 'DeviceInterfaceTable',
  19. 'DeviceInventoryItemTable',
  20. 'DeviceModuleBayTable',
  21. 'DevicePowerPortTable',
  22. 'DevicePowerOutletTable',
  23. 'DeviceRearPortTable',
  24. 'DeviceRoleTable',
  25. 'DeviceTable',
  26. 'FrontPortTable',
  27. 'InterfaceTable',
  28. 'InventoryItemRoleTable',
  29. 'InventoryItemTable',
  30. 'ModuleBayTable',
  31. 'PlatformTable',
  32. 'PowerOutletTable',
  33. 'PowerPortTable',
  34. 'RearPortTable',
  35. 'VirtualChassisTable',
  36. 'VirtualDeviceContextTable'
  37. )
  38. MODULEBAY_STATUS = """
  39. {% badge record.installed_module.get_status_display bg_color=record.installed_module.get_status_color %}
  40. """
  41. def get_cabletermination_row_class(record):
  42. if record.mark_connected:
  43. return 'success'
  44. elif record.cable:
  45. return record.cable.get_status_color()
  46. return ''
  47. #
  48. # Device roles
  49. #
  50. class DeviceRoleTable(NetBoxTable):
  51. name = tables.Column(
  52. verbose_name=_('Name'),
  53. linkify=True
  54. )
  55. device_count = columns.LinkedCountColumn(
  56. viewname='dcim:device_list',
  57. url_params={'role_id': 'pk'},
  58. verbose_name=_('Devices')
  59. )
  60. vm_count = columns.LinkedCountColumn(
  61. viewname='virtualization:virtualmachine_list',
  62. url_params={'role_id': 'pk'},
  63. verbose_name=_('VMs')
  64. )
  65. color = columns.ColorColumn()
  66. vm_role = columns.BooleanColumn()
  67. config_template = tables.Column(
  68. linkify=True
  69. )
  70. tags = columns.TagColumn(
  71. url_name='dcim:devicerole_list'
  72. )
  73. class Meta(NetBoxTable.Meta):
  74. model = models.DeviceRole
  75. fields = (
  76. 'pk', 'id', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'config_template', 'description',
  77. 'slug', 'tags', 'actions', 'created', 'last_updated',
  78. )
  79. default_columns = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description')
  80. #
  81. # Platforms
  82. #
  83. class PlatformTable(NetBoxTable):
  84. name = tables.Column(
  85. verbose_name=_('Name'),
  86. linkify=True
  87. )
  88. manufacturer = tables.Column(
  89. verbose_name=_('Manufacturer'),
  90. linkify=True
  91. )
  92. config_template = tables.Column(
  93. verbose_name=_('Config Template'),
  94. linkify=True
  95. )
  96. device_count = columns.LinkedCountColumn(
  97. viewname='dcim:device_list',
  98. url_params={'platform_id': 'pk'},
  99. verbose_name=_('Devices')
  100. )
  101. vm_count = columns.LinkedCountColumn(
  102. viewname='virtualization:virtualmachine_list',
  103. url_params={'platform_id': 'pk'},
  104. verbose_name=_('VMs')
  105. )
  106. tags = columns.TagColumn(
  107. url_name='dcim:platform_list'
  108. )
  109. class Meta(NetBoxTable.Meta):
  110. model = models.Platform
  111. fields = (
  112. 'pk', 'id', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'config_template', 'description',
  113. 'tags', 'actions', 'created', 'last_updated',
  114. )
  115. default_columns = (
  116. 'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'description',
  117. )
  118. #
  119. # Devices
  120. #
  121. class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
  122. name = tables.TemplateColumn(
  123. verbose_name=_('Name'),
  124. order_by=('_name',),
  125. template_code=DEVICE_LINK,
  126. linkify=True
  127. )
  128. status = columns.ChoiceFieldColumn(
  129. verbose_name=_('Status'),
  130. )
  131. region = tables.Column(
  132. verbose_name=_('Region'),
  133. accessor=Accessor('site__region'),
  134. linkify=True
  135. )
  136. site_group = tables.Column(
  137. accessor=Accessor('site__group'),
  138. linkify=True,
  139. verbose_name=_('Site Group')
  140. )
  141. site = tables.Column(
  142. verbose_name=_('Site'),
  143. linkify=True
  144. )
  145. location = tables.Column(
  146. verbose_name=_('Location'),
  147. linkify=True
  148. )
  149. rack = tables.Column(
  150. verbose_name=_('Rack'),
  151. linkify=True
  152. )
  153. position = columns.TemplateColumn(
  154. verbose_name=_('Position'),
  155. template_code='{{ value|floatformat }}'
  156. )
  157. role = columns.ColoredLabelColumn(
  158. verbose_name=_('Role')
  159. )
  160. manufacturer = tables.Column(
  161. verbose_name=_('Manufacturer'),
  162. accessor=Accessor('device_type__manufacturer'),
  163. linkify=True
  164. )
  165. device_type = tables.Column(
  166. linkify=True,
  167. verbose_name=_('Type')
  168. )
  169. platform = tables.Column(
  170. linkify=True,
  171. verbose_name=_('Platform')
  172. )
  173. primary_ip = tables.Column(
  174. linkify=True,
  175. order_by=('primary_ip4', 'primary_ip6'),
  176. verbose_name=_('IP Address')
  177. )
  178. primary_ip4 = tables.Column(
  179. linkify=True,
  180. verbose_name=_('IPv4 Address')
  181. )
  182. primary_ip6 = tables.Column(
  183. linkify=True,
  184. verbose_name=_('IPv6 Address')
  185. )
  186. oob_ip = tables.Column(
  187. linkify=True,
  188. verbose_name='OOB IP'
  189. )
  190. cluster = tables.Column(
  191. verbose_name=_('Cluster'),
  192. linkify=True
  193. )
  194. virtual_chassis = tables.Column(
  195. verbose_name=_('Virtual Chassis'),
  196. linkify=True
  197. )
  198. vc_position = tables.Column(
  199. verbose_name=_('VC Position')
  200. )
  201. vc_priority = tables.Column(
  202. verbose_name=_('VC Priority')
  203. )
  204. config_template = tables.Column(
  205. verbose_name=_('Config Template'),
  206. linkify=True
  207. )
  208. parent_device = tables.Column(
  209. verbose_name=_('Parent Device'),
  210. linkify=True,
  211. accessor='parent_bay__device'
  212. )
  213. device_bay_position = tables.Column(
  214. verbose_name=_('Position (Device Bay)'),
  215. accessor='parent_bay',
  216. linkify=True
  217. )
  218. comments = columns.MarkdownColumn()
  219. tags = columns.TagColumn(
  220. url_name='dcim:device_list'
  221. )
  222. console_port_count = tables.Column(
  223. verbose_name=_('Console ports')
  224. )
  225. console_server_port_count = tables.Column(
  226. verbose_name=_('Console server ports')
  227. )
  228. power_port_count = tables.Column(
  229. verbose_name=_('Power ports')
  230. )
  231. power_outlet_count = tables.Column(
  232. verbose_name=_('Power outlets')
  233. )
  234. interface_count = tables.Column(
  235. verbose_name=_('Interfaces')
  236. )
  237. front_port_count = tables.Column(
  238. verbose_name=_('Front ports')
  239. )
  240. rear_port_count = tables.Column(
  241. verbose_name=_('Rear ports')
  242. )
  243. device_bay_count = tables.Column(
  244. verbose_name=_('Device bays')
  245. )
  246. module_bay_count = tables.Column(
  247. verbose_name=_('Module bays')
  248. )
  249. inventory_item_count = tables.Column(
  250. verbose_name=_('Inventory items')
  251. )
  252. class Meta(NetBoxTable.Meta):
  253. model = models.Device
  254. fields = (
  255. 'pk', 'id', 'name', 'status', 'tenant', 'tenant_group', 'role', 'manufacturer', 'device_type',
  256. 'serial', 'asset_tag', 'region', 'site_group', 'site', 'location', 'rack', 'parent_device',
  257. 'device_bay_position', 'position', 'face', 'latitude', 'longitude', 'airflow', 'primary_ip', 'primary_ip4',
  258. 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'description',
  259. 'config_template', 'comments', 'contacts', 'tags', 'created', 'last_updated',
  260. )
  261. default_columns = (
  262. 'pk', 'name', 'status', 'tenant', 'site', 'location', 'rack', 'role', 'manufacturer', 'device_type',
  263. 'primary_ip',
  264. )
  265. #
  266. # Device components
  267. #
  268. class DeviceComponentTable(NetBoxTable):
  269. device = tables.Column(
  270. verbose_name=_('Device'),
  271. linkify=True
  272. )
  273. name = tables.Column(
  274. verbose_name=_('Name'),
  275. linkify=True,
  276. order_by=('_name',)
  277. )
  278. class Meta(NetBoxTable.Meta):
  279. order_by = ('device', 'name')
  280. class ModularDeviceComponentTable(DeviceComponentTable):
  281. module_bay = tables.Column(
  282. verbose_name=_('Module Bay'),
  283. accessor=Accessor('module__module_bay'),
  284. linkify={
  285. 'viewname': 'dcim:device_modulebays',
  286. 'args': [Accessor('device_id')],
  287. }
  288. )
  289. module = tables.Column(
  290. verbose_name=_('Module'),
  291. linkify=True
  292. )
  293. inventory_items = columns.ManyToManyColumn(
  294. linkify_item=True,
  295. verbose_name=_('Inventory Items'),
  296. )
  297. class CableTerminationTable(NetBoxTable):
  298. cable = tables.Column(
  299. verbose_name=_('Cable'),
  300. linkify=True
  301. )
  302. cable_color = columns.ColorColumn(
  303. accessor='cable__color',
  304. orderable=False,
  305. verbose_name=_('Cable Color')
  306. )
  307. link_peer = columns.TemplateColumn(
  308. accessor='link_peers',
  309. template_code=LINKTERMINATION,
  310. orderable=False,
  311. verbose_name=_('Link Peers')
  312. )
  313. mark_connected = columns.BooleanColumn(
  314. verbose_name=_('Mark Connected'),
  315. )
  316. def value_link_peer(self, value):
  317. return ', '.join([
  318. f"{termination.parent_object} > {termination}" for termination in value
  319. ])
  320. class PathEndpointTable(CableTerminationTable):
  321. connection = columns.TemplateColumn(
  322. accessor='_path__destinations',
  323. template_code=LINKTERMINATION,
  324. verbose_name=_('Connection'),
  325. orderable=False
  326. )
  327. class ConsolePortTable(ModularDeviceComponentTable, PathEndpointTable):
  328. device = tables.Column(
  329. verbose_name=_('Device'),
  330. linkify={
  331. 'viewname': 'dcim:device_consoleports',
  332. 'args': [Accessor('device_id')],
  333. }
  334. )
  335. tags = columns.TagColumn(
  336. url_name='dcim:consoleport_list'
  337. )
  338. class Meta(DeviceComponentTable.Meta):
  339. model = models.ConsolePort
  340. fields = (
  341. 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description',
  342. 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items', 'tags', 'created', 'last_updated',
  343. )
  344. default_columns = ('pk', 'name', 'device', 'label', 'type', 'speed', 'description')
  345. class DeviceConsolePortTable(ConsolePortTable):
  346. name = tables.TemplateColumn(
  347. verbose_name=_('Name'),
  348. template_code='<i class="mdi mdi-console"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
  349. order_by=Accessor('_name'),
  350. attrs={'td': {'class': 'text-nowrap'}}
  351. )
  352. actions = columns.ActionsColumn(
  353. extra_buttons=CONSOLEPORT_BUTTONS
  354. )
  355. class Meta(DeviceComponentTable.Meta):
  356. model = models.ConsolePort
  357. fields = (
  358. 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'speed', 'description', 'mark_connected',
  359. 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions'
  360. )
  361. default_columns = ('pk', 'name', 'label', 'type', 'speed', 'description', 'cable', 'connection')
  362. row_attrs = {
  363. 'class': get_cabletermination_row_class
  364. }
  365. class ConsoleServerPortTable(ModularDeviceComponentTable, PathEndpointTable):
  366. device = tables.Column(
  367. verbose_name=_('Device'),
  368. linkify={
  369. 'viewname': 'dcim:device_consoleserverports',
  370. 'args': [Accessor('device_id')],
  371. }
  372. )
  373. tags = columns.TagColumn(
  374. url_name='dcim:consoleserverport_list'
  375. )
  376. class Meta(DeviceComponentTable.Meta):
  377. model = models.ConsoleServerPort
  378. fields = (
  379. 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description',
  380. 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items', 'tags', 'created', 'last_updated',
  381. )
  382. default_columns = ('pk', 'name', 'device', 'label', 'type', 'speed', 'description')
  383. class DeviceConsoleServerPortTable(ConsoleServerPortTable):
  384. name = tables.TemplateColumn(
  385. verbose_name=_('Name'),
  386. template_code='<i class="mdi mdi-console-network-outline"></i> '
  387. '<a href="{{ record.get_absolute_url }}">{{ value }}</a>',
  388. order_by=Accessor('_name'),
  389. attrs={'td': {'class': 'text-nowrap'}}
  390. )
  391. actions = columns.ActionsColumn(
  392. extra_buttons=CONSOLESERVERPORT_BUTTONS
  393. )
  394. class Meta(DeviceComponentTable.Meta):
  395. model = models.ConsoleServerPort
  396. fields = (
  397. 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'speed', 'description', 'mark_connected',
  398. 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
  399. )
  400. default_columns = ('pk', 'name', 'label', 'type', 'speed', 'description', 'cable', 'connection')
  401. row_attrs = {
  402. 'class': get_cabletermination_row_class
  403. }
  404. class PowerPortTable(ModularDeviceComponentTable, PathEndpointTable):
  405. device = tables.Column(
  406. verbose_name=_('Device'),
  407. linkify={
  408. 'viewname': 'dcim:device_powerports',
  409. 'args': [Accessor('device_id')],
  410. }
  411. )
  412. maximum_draw = tables.Column(
  413. verbose_name=_('Maximum draw (W)')
  414. )
  415. allocated_draw = tables.Column(
  416. verbose_name=_('Allocated draw (W)')
  417. )
  418. tags = columns.TagColumn(
  419. url_name='dcim:powerport_list'
  420. )
  421. class Meta(DeviceComponentTable.Meta):
  422. model = models.PowerPort
  423. fields = (
  424. 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'mark_connected',
  425. 'maximum_draw', 'allocated_draw', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items',
  426. 'tags', 'created', 'last_updated',
  427. )
  428. default_columns = ('pk', 'name', 'device', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description')
  429. class DevicePowerPortTable(PowerPortTable):
  430. name = tables.TemplateColumn(
  431. verbose_name=_('Name'),
  432. template_code='<i class="mdi mdi-power-plug-outline"></i> <a href="{{ record.get_absolute_url }}">'
  433. '{{ value }}</a>',
  434. order_by=Accessor('_name'),
  435. attrs={'td': {'class': 'text-nowrap'}}
  436. )
  437. actions = columns.ActionsColumn(
  438. extra_buttons=POWERPORT_BUTTONS
  439. )
  440. class Meta(DeviceComponentTable.Meta):
  441. model = models.PowerPort
  442. fields = (
  443. 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'maximum_draw', 'allocated_draw',
  444. 'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
  445. )
  446. default_columns = (
  447. 'pk', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'cable', 'connection',
  448. )
  449. row_attrs = {
  450. 'class': get_cabletermination_row_class
  451. }
  452. class PowerOutletTable(ModularDeviceComponentTable, PathEndpointTable):
  453. device = tables.Column(
  454. verbose_name=_('Device'),
  455. linkify={
  456. 'viewname': 'dcim:device_poweroutlets',
  457. 'args': [Accessor('device_id')],
  458. }
  459. )
  460. power_port = tables.Column(
  461. verbose_name=_('Power Port'),
  462. linkify=True
  463. )
  464. tags = columns.TagColumn(
  465. url_name='dcim:poweroutlet_list'
  466. )
  467. class Meta(DeviceComponentTable.Meta):
  468. model = models.PowerOutlet
  469. fields = (
  470. 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'power_port',
  471. 'feed_leg', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items',
  472. 'tags', 'created', 'last_updated',
  473. )
  474. default_columns = ('pk', 'name', 'device', 'label', 'type', 'power_port', 'feed_leg', 'description')
  475. class DevicePowerOutletTable(PowerOutletTable):
  476. name = tables.TemplateColumn(
  477. verbose_name=_('Name'),
  478. template_code='<i class="mdi mdi-power-socket"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
  479. order_by=Accessor('_name'),
  480. attrs={'td': {'class': 'text-nowrap'}}
  481. )
  482. actions = columns.ActionsColumn(
  483. extra_buttons=POWEROUTLET_BUTTONS
  484. )
  485. class Meta(DeviceComponentTable.Meta):
  486. model = models.PowerOutlet
  487. fields = (
  488. 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'power_port', 'feed_leg', 'description',
  489. 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
  490. )
  491. default_columns = (
  492. 'pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'cable', 'connection',
  493. )
  494. row_attrs = {
  495. 'class': get_cabletermination_row_class
  496. }
  497. class BaseInterfaceTable(NetBoxTable):
  498. enabled = columns.BooleanColumn(
  499. verbose_name=_('Enabled'),
  500. )
  501. ip_addresses = tables.TemplateColumn(
  502. template_code=INTERFACE_IPADDRESSES,
  503. orderable=False,
  504. verbose_name=_('IP Addresses')
  505. )
  506. fhrp_groups = tables.TemplateColumn(
  507. accessor=Accessor('fhrp_group_assignments'),
  508. template_code=INTERFACE_FHRPGROUPS,
  509. orderable=False,
  510. verbose_name=_('FHRP Groups')
  511. )
  512. l2vpn = tables.Column(
  513. accessor=tables.A('l2vpn_termination__l2vpn'),
  514. linkify=True,
  515. orderable=False,
  516. verbose_name=_('L2VPN')
  517. )
  518. tunnel = tables.Column(
  519. accessor=tables.A('tunnel_termination__tunnel'),
  520. linkify=True,
  521. orderable=False,
  522. verbose_name=_('Tunnel')
  523. )
  524. untagged_vlan = tables.Column(
  525. verbose_name=_('Untagged VLAN'),
  526. linkify=True
  527. )
  528. tagged_vlans = columns.TemplateColumn(
  529. template_code=INTERFACE_TAGGED_VLANS,
  530. orderable=False,
  531. verbose_name=_('Tagged VLANs')
  532. )
  533. def value_ip_addresses(self, value):
  534. return ",".join([str(obj.address) for obj in value.all()])
  535. class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpointTable):
  536. device = tables.Column(
  537. verbose_name=_('Device'),
  538. linkify={
  539. 'viewname': 'dcim:device_interfaces',
  540. 'args': [Accessor('device_id')],
  541. }
  542. )
  543. mgmt_only = columns.BooleanColumn(
  544. verbose_name=_('Management Only')
  545. )
  546. speed_formatted = columns.TemplateColumn(
  547. template_code='{% load helpers %}{{ value|humanize_speed }}',
  548. accessor=Accessor('speed'),
  549. verbose_name=_('Speed')
  550. )
  551. wireless_link = tables.Column(
  552. verbose_name=_('Wireless link'),
  553. linkify=True
  554. )
  555. wireless_lans = columns.TemplateColumn(
  556. template_code=INTERFACE_WIRELESS_LANS,
  557. orderable=False,
  558. verbose_name=_('Wireless LANs')
  559. )
  560. vdcs = columns.ManyToManyColumn(
  561. linkify_item=True,
  562. verbose_name=_('VDCs')
  563. )
  564. vrf = tables.Column(
  565. verbose_name=_('VRF'),
  566. linkify=True
  567. )
  568. tags = columns.TagColumn(
  569. url_name='dcim:interface_list'
  570. )
  571. class Meta(DeviceComponentTable.Meta):
  572. model = models.Interface
  573. fields = (
  574. 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'enabled', 'type', 'mgmt_only', 'mtu',
  575. 'speed', 'speed_formatted', 'duplex', 'mode', 'mac_address', 'wwn', 'poe_mode', 'poe_type', 'rf_role', 'rf_channel',
  576. 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable',
  577. 'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn',
  578. 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'inventory_items', 'created',
  579. 'last_updated',
  580. )
  581. default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description')
  582. class DeviceInterfaceTable(InterfaceTable):
  583. name = tables.TemplateColumn(
  584. verbose_name=_('Name'),
  585. template_code='<i class="mdi mdi-{% if record.mgmt_only %}wrench{% elif record.is_lag %}reorder-horizontal'
  586. '{% elif record.is_virtual %}circle{% elif record.is_wireless %}wifi{% else %}ethernet'
  587. '{% endif %}"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
  588. order_by=Accessor('_name'),
  589. attrs={'td': {'class': 'text-nowrap'}}
  590. )
  591. parent = tables.Column(
  592. verbose_name=_('Parent'),
  593. linkify=True
  594. )
  595. bridge = tables.Column(
  596. verbose_name=_('Bridge'),
  597. linkify=True
  598. )
  599. lag = tables.Column(
  600. linkify=True,
  601. verbose_name=_('LAG')
  602. )
  603. actions = columns.ActionsColumn(
  604. extra_buttons=INTERFACE_BUTTONS
  605. )
  606. class Meta(DeviceComponentTable.Meta):
  607. model = models.Interface
  608. fields = (
  609. 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'enabled', 'type', 'parent', 'bridge', 'lag',
  610. 'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency',
  611. 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link',
  612. 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses',
  613. 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'actions',
  614. )
  615. default_columns = (
  616. 'pk', 'name', 'label', 'enabled', 'type', 'parent', 'lag', 'mtu', 'mode', 'description', 'ip_addresses',
  617. 'cable', 'connection',
  618. )
  619. row_attrs = {
  620. 'data-name': lambda record: record.name,
  621. 'data-enabled': lambda record: "enabled" if record.enabled else "disabled",
  622. 'data-virtual': lambda record: "true" if record.is_virtual else "false",
  623. 'data-mark-connected': lambda record: "true" if record.mark_connected else "false",
  624. 'data-cable-status': lambda record: record.cable.status if record.cable else "",
  625. 'data-type': lambda record: record.type
  626. }
  627. class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable):
  628. device = tables.Column(
  629. verbose_name=_('Device'),
  630. linkify={
  631. 'viewname': 'dcim:device_frontports',
  632. 'args': [Accessor('device_id')],
  633. }
  634. )
  635. color = columns.ColorColumn(
  636. verbose_name=_('Color'),
  637. )
  638. rear_port_position = tables.Column(
  639. verbose_name=_('Position')
  640. )
  641. rear_port = tables.Column(
  642. verbose_name=_('Rear Port'),
  643. linkify=True
  644. )
  645. tags = columns.TagColumn(
  646. url_name='dcim:frontport_list'
  647. )
  648. class Meta(DeviceComponentTable.Meta):
  649. model = models.FrontPort
  650. fields = (
  651. 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'rear_port',
  652. 'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_color', 'link_peer',
  653. 'inventory_items', 'tags', 'created', 'last_updated',
  654. )
  655. default_columns = (
  656. 'pk', 'name', 'device', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description',
  657. )
  658. class DeviceFrontPortTable(FrontPortTable):
  659. name = tables.TemplateColumn(
  660. verbose_name=_('Name'),
  661. template_code='<i class="mdi mdi-square-rounded{% if not record.cable %}-outline{% endif %}"></i> '
  662. '<a href="{{ record.get_absolute_url }}">{{ value }}</a>',
  663. order_by=Accessor('_name'),
  664. attrs={'td': {'class': 'text-nowrap'}}
  665. )
  666. actions = columns.ActionsColumn(
  667. extra_buttons=FRONTPORT_BUTTONS
  668. )
  669. class Meta(DeviceComponentTable.Meta):
  670. model = models.FrontPort
  671. fields = (
  672. 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'rear_port', 'rear_port_position',
  673. 'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags', 'actions',
  674. )
  675. default_columns = (
  676. 'pk', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', 'link_peer',
  677. )
  678. row_attrs = {
  679. 'class': get_cabletermination_row_class
  680. }
  681. class RearPortTable(ModularDeviceComponentTable, CableTerminationTable):
  682. device = tables.Column(
  683. verbose_name=_('Device'),
  684. linkify={
  685. 'viewname': 'dcim:device_rearports',
  686. 'args': [Accessor('device_id')],
  687. }
  688. )
  689. color = columns.ColorColumn(
  690. verbose_name=_('Color'),
  691. )
  692. tags = columns.TagColumn(
  693. url_name='dcim:rearport_list'
  694. )
  695. class Meta(DeviceComponentTable.Meta):
  696. model = models.RearPort
  697. fields = (
  698. 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'description',
  699. 'mark_connected', 'cable', 'cable_color', 'link_peer', 'inventory_items', 'tags', 'created', 'last_updated',
  700. )
  701. default_columns = ('pk', 'name', 'device', 'label', 'type', 'color', 'description')
  702. class DeviceRearPortTable(RearPortTable):
  703. name = tables.TemplateColumn(
  704. verbose_name=_('Name'),
  705. template_code='<i class="mdi mdi-square-rounded{% if not record.cable %}-outline{% endif %}"></i> '
  706. '<a href="{{ record.get_absolute_url }}">{{ value }}</a>',
  707. order_by=Accessor('_name'),
  708. attrs={'td': {'class': 'text-nowrap'}}
  709. )
  710. actions = columns.ActionsColumn(
  711. extra_buttons=REARPORT_BUTTONS
  712. )
  713. class Meta(DeviceComponentTable.Meta):
  714. model = models.RearPort
  715. fields = (
  716. 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'positions', 'description', 'mark_connected',
  717. 'cable', 'cable_color', 'link_peer', 'tags', 'actions',
  718. )
  719. default_columns = (
  720. 'pk', 'name', 'label', 'type', 'positions', 'description', 'cable', 'link_peer',
  721. )
  722. row_attrs = {
  723. 'class': get_cabletermination_row_class
  724. }
  725. class DeviceBayTable(DeviceComponentTable):
  726. device = tables.Column(
  727. verbose_name=_('Device'),
  728. linkify={
  729. 'viewname': 'dcim:device_devicebays',
  730. 'args': [Accessor('device_id')],
  731. }
  732. )
  733. role = columns.ColoredLabelColumn(
  734. accessor=Accessor('installed_device__role'),
  735. verbose_name=_('Role')
  736. )
  737. device_type = tables.Column(
  738. accessor=Accessor('installed_device__device_type'),
  739. linkify=True,
  740. verbose_name=_('Type')
  741. )
  742. status = tables.TemplateColumn(
  743. verbose_name=_('Status'),
  744. template_code=DEVICEBAY_STATUS,
  745. order_by=Accessor('installed_device__status')
  746. )
  747. installed_device = tables.Column(
  748. verbose_name=_('Installed device'),
  749. linkify=True
  750. )
  751. tags = columns.TagColumn(
  752. url_name='dcim:devicebay_list'
  753. )
  754. class Meta(DeviceComponentTable.Meta):
  755. model = models.DeviceBay
  756. fields = (
  757. 'pk', 'id', 'name', 'device', 'label', 'status', 'role', 'device_type', 'installed_device', 'description',
  758. 'tags', 'created', 'last_updated',
  759. )
  760. default_columns = ('pk', 'name', 'device', 'label', 'status', 'installed_device', 'description')
  761. class DeviceDeviceBayTable(DeviceBayTable):
  762. name = tables.TemplateColumn(
  763. verbose_name=_('Name'),
  764. template_code='<i class="mdi mdi-circle{% if record.installed_device %}slice-8{% else %}outline{% endif %}'
  765. '"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
  766. order_by=Accessor('_name'),
  767. attrs={'td': {'class': 'text-nowrap'}}
  768. )
  769. actions = columns.ActionsColumn(
  770. extra_buttons=DEVICEBAY_BUTTONS
  771. )
  772. class Meta(DeviceComponentTable.Meta):
  773. model = models.DeviceBay
  774. fields = (
  775. 'pk', 'id', 'name', 'label', 'status', 'installed_device', 'description', 'tags', 'actions',
  776. )
  777. default_columns = ('pk', 'name', 'label', 'status', 'installed_device', 'description')
  778. class ModuleBayTable(DeviceComponentTable):
  779. device = tables.Column(
  780. verbose_name=_('Device'),
  781. linkify={
  782. 'viewname': 'dcim:device_modulebays',
  783. 'args': [Accessor('device_id')],
  784. }
  785. )
  786. installed_module = tables.Column(
  787. linkify=True,
  788. verbose_name=_('Installed Module')
  789. )
  790. module_serial = tables.Column(
  791. verbose_name=_('Module Serial'),
  792. accessor=tables.A('installed_module__serial')
  793. )
  794. module_asset_tag = tables.Column(
  795. verbose_name=_('Module Asset Tag'),
  796. accessor=tables.A('installed_module__asset_tag')
  797. )
  798. tags = columns.TagColumn(
  799. url_name='dcim:modulebay_list'
  800. )
  801. module_status = columns.TemplateColumn(
  802. accessor=tables.A('installed_module__status'),
  803. template_code=MODULEBAY_STATUS,
  804. verbose_name=_('Module Status')
  805. )
  806. class Meta(DeviceComponentTable.Meta):
  807. model = models.ModuleBay
  808. fields = (
  809. 'pk', 'id', 'name', 'device', 'label', 'position', 'installed_module', 'module_status', 'module_serial',
  810. 'module_asset_tag', 'description', 'tags',
  811. )
  812. default_columns = ('pk', 'name', 'device', 'label', 'installed_module', 'module_status', 'description')
  813. class DeviceModuleBayTable(ModuleBayTable):
  814. actions = columns.ActionsColumn(
  815. extra_buttons=MODULEBAY_BUTTONS
  816. )
  817. class Meta(DeviceComponentTable.Meta):
  818. model = models.ModuleBay
  819. fields = (
  820. 'pk', 'id', 'name', 'label', 'position', 'installed_module', 'module_status', 'module_serial', 'module_asset_tag',
  821. 'description', 'tags', 'actions',
  822. )
  823. default_columns = ('pk', 'name', 'label', 'installed_module', 'module_status', 'description')
  824. class InventoryItemTable(DeviceComponentTable):
  825. device = tables.Column(
  826. verbose_name=_('Device'),
  827. linkify={
  828. 'viewname': 'dcim:device_inventory',
  829. 'args': [Accessor('device_id')],
  830. }
  831. )
  832. role = columns.ColoredLabelColumn(
  833. verbose_name=_('Role'),
  834. )
  835. manufacturer = tables.Column(
  836. verbose_name=_('Manufacturer'),
  837. linkify=True
  838. )
  839. component = tables.Column(
  840. verbose_name=_('Component'),
  841. orderable=False,
  842. linkify=True
  843. )
  844. discovered = columns.BooleanColumn(
  845. verbose_name=_('Discovered'),
  846. )
  847. parent = tables.Column(
  848. linkify=True,
  849. verbose_name=_('Parent'),
  850. )
  851. tags = columns.TagColumn(
  852. url_name='dcim:inventoryitem_list'
  853. )
  854. cable = None # Override DeviceComponentTable
  855. class Meta(NetBoxTable.Meta):
  856. model = models.InventoryItem
  857. fields = (
  858. 'pk', 'id', 'name', 'device', 'parent', 'component', 'label', 'role', 'manufacturer', 'part_id', 'serial',
  859. 'asset_tag', 'description', 'discovered', 'tags', 'created', 'last_updated',
  860. )
  861. default_columns = (
  862. 'pk', 'name', 'device', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
  863. )
  864. class DeviceInventoryItemTable(InventoryItemTable):
  865. name = tables.TemplateColumn(
  866. verbose_name=_('Name'),
  867. template_code='<a href="{{ record.get_absolute_url }}" style="padding-left: {{ record.level }}0px">'
  868. '{{ value }}</a>',
  869. order_by=Accessor('_name'),
  870. attrs={'td': {'class': 'text-nowrap'}}
  871. )
  872. class Meta(NetBoxTable.Meta):
  873. model = models.InventoryItem
  874. fields = (
  875. 'pk', 'id', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component',
  876. 'description', 'discovered', 'tags', 'actions',
  877. )
  878. default_columns = (
  879. 'pk', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component',
  880. )
  881. class InventoryItemRoleTable(NetBoxTable):
  882. name = tables.Column(
  883. verbose_name=_('Name'),
  884. linkify=True
  885. )
  886. inventoryitem_count = columns.LinkedCountColumn(
  887. viewname='dcim:inventoryitem_list',
  888. url_params={'role_id': 'pk'},
  889. verbose_name=_('Items')
  890. )
  891. color = columns.ColorColumn(
  892. verbose_name=_('Color'),
  893. )
  894. tags = columns.TagColumn(
  895. url_name='dcim:inventoryitemrole_list'
  896. )
  897. class Meta(NetBoxTable.Meta):
  898. model = models.InventoryItemRole
  899. fields = (
  900. 'pk', 'id', 'name', 'inventoryitem_count', 'color', 'description', 'slug', 'tags', 'actions',
  901. )
  902. default_columns = ('pk', 'name', 'inventoryitem_count', 'color', 'description')
  903. #
  904. # Virtual chassis
  905. #
  906. class VirtualChassisTable(NetBoxTable):
  907. name = tables.Column(
  908. verbose_name=_('Name'),
  909. linkify=True
  910. )
  911. master = tables.Column(
  912. verbose_name=_('Master'),
  913. linkify=True
  914. )
  915. member_count = columns.LinkedCountColumn(
  916. viewname='dcim:device_list',
  917. url_params={'virtual_chassis_id': 'pk'},
  918. verbose_name=_('Members')
  919. )
  920. comments = columns.MarkdownColumn(
  921. verbose_name=_('Comments'),
  922. )
  923. tags = columns.TagColumn(
  924. url_name='dcim:virtualchassis_list'
  925. )
  926. class Meta(NetBoxTable.Meta):
  927. model = models.VirtualChassis
  928. fields = (
  929. 'pk', 'id', 'name', 'domain', 'master', 'member_count', 'description', 'comments', 'tags', 'created',
  930. 'last_updated',
  931. )
  932. default_columns = ('pk', 'name', 'domain', 'master', 'member_count')
  933. class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable):
  934. name = tables.Column(
  935. verbose_name=_('Name'),
  936. linkify=True
  937. )
  938. device = tables.TemplateColumn(
  939. verbose_name=_('Device'),
  940. order_by=('_name',),
  941. template_code=DEVICE_LINK,
  942. linkify=True
  943. )
  944. status = columns.ChoiceFieldColumn(
  945. verbose_name=_('Status'),
  946. )
  947. primary_ip = tables.Column(
  948. linkify=True,
  949. order_by=('primary_ip4', 'primary_ip6'),
  950. verbose_name=_('IP Address')
  951. )
  952. primary_ip4 = tables.Column(
  953. linkify=True,
  954. verbose_name=_('IPv4 Address')
  955. )
  956. primary_ip6 = tables.Column(
  957. linkify=True,
  958. verbose_name=_('IPv6 Address')
  959. )
  960. interface_count = columns.LinkedCountColumn(
  961. viewname='dcim:interface_list',
  962. url_params={'vdc_id': 'pk'},
  963. verbose_name=_('Interfaces')
  964. )
  965. comments = columns.MarkdownColumn()
  966. tags = columns.TagColumn(
  967. url_name='dcim:virtualdevicecontext_list'
  968. )
  969. class Meta(NetBoxTable.Meta):
  970. model = models.VirtualDeviceContext
  971. fields = (
  972. 'pk', 'id', 'name', 'status', 'identifier', 'tenant', 'tenant_group', 'primary_ip', 'primary_ip4',
  973. 'primary_ip6', 'comments', 'tags', 'interface_count', 'created', 'last_updated',
  974. )
  975. default_columns = (
  976. 'pk', 'name', 'identifier', 'status', 'tenant', 'primary_ip',
  977. )