devices.py 37 KB

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