devices.py 37 KB

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