tables.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. import django_tables2 as tables
  2. from django_tables2.utils import Accessor
  3. from dcim.models import Interface
  4. from tenancy.tables import COL_TENANT
  5. from utilities.tables import BaseTable, BooleanColumn, TagColumn, ToggleColumn
  6. from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
  7. RIR_UTILIZATION = """
  8. <div class="progress">
  9. {% if record.stats.total %}
  10. <div class="progress-bar" role="progressbar" style="width: {{ record.stats.percentages.active }}%;">
  11. <span class="sr-only">{{ record.stats.percentages.active }}%</span>
  12. </div>
  13. <div class="progress-bar progress-bar-info" role="progressbar" style="width: {{ record.stats.percentages.reserved }}%;">
  14. <span class="sr-only">{{ record.stats.percentages.reserved }}%</span>
  15. </div>
  16. <div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{ record.stats.percentages.deprecated }}%;">
  17. <span class="sr-only">{{ record.stats.percentages.deprecated }}%</span>
  18. </div>
  19. <div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ record.stats.percentages.available }}%;">
  20. <span class="sr-only">{{ record.stats.percentages.available }}%</span>
  21. </div>
  22. {% endif %}
  23. </div>
  24. """
  25. RIR_ACTIONS = """
  26. <a href="{% url 'ipam:rir_changelog' slug=record.slug %}" class="btn btn-default btn-xs" title="Change log">
  27. <i class="fa fa-history"></i>
  28. </a>
  29. {% if perms.ipam.change_rir %}
  30. <a href="{% url 'ipam:rir_edit' slug=record.slug %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
  31. {% endif %}
  32. """
  33. UTILIZATION_GRAPH = """
  34. {% load helpers %}
  35. {% if record.pk %}{% utilization_graph record.get_utilization %}{% else %}&mdash;{% endif %}
  36. """
  37. ROLE_PREFIX_COUNT = """
  38. <a href="{% url 'ipam:prefix_list' %}?role={{ record.slug }}">{{ value }}</a>
  39. """
  40. ROLE_VLAN_COUNT = """
  41. <a href="{% url 'ipam:vlan_list' %}?role={{ record.slug }}">{{ value }}</a>
  42. """
  43. ROLE_ACTIONS = """
  44. <a href="{% url 'ipam:role_changelog' slug=record.slug %}" class="btn btn-default btn-xs" title="Change log">
  45. <i class="fa fa-history"></i>
  46. </a>
  47. {% if perms.ipam.change_role %}
  48. <a href="{% url 'ipam:role_edit' slug=record.slug %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
  49. {% endif %}
  50. """
  51. PREFIX_LINK = """
  52. {% if record.has_children %}
  53. <span class="text-nowrap" style="padding-left: {{ record.depth }}0px "><i class="fa fa-caret-right"></i></a>
  54. {% else %}
  55. <span class="text-nowrap" style="padding-left: {{ record.depth }}9px">
  56. {% endif %}
  57. <a href="{% if record.pk %}{% url 'ipam:prefix' pk=record.pk %}{% else %}{% url 'ipam:prefix_add' %}?prefix={{ record }}{% if parent.vrf %}&vrf={{ parent.vrf.pk }}{% endif %}{% if parent.site %}&site={{ parent.site.pk }}{% endif %}{% if parent.tenant %}&tenant_group={{ parent.tenant.group.pk }}&tenant={{ parent.tenant.pk }}{% endif %}{% endif %}">{{ record.prefix }}</a>
  58. </span>
  59. """
  60. PREFIX_ROLE_LINK = """
  61. {% if record.role %}
  62. <a href="{% url 'ipam:prefix_list' %}?role={{ record.role.slug }}">{{ record.role }}</a>
  63. {% else %}
  64. &mdash;
  65. {% endif %}
  66. """
  67. IPADDRESS_LINK = """
  68. {% if record.pk %}
  69. <a href="{{ record.get_absolute_url }}">{{ record.address }}</a>
  70. {% elif perms.ipam.add_ipaddress %}
  71. <a href="{% url 'ipam:ipaddress_add' %}?address={{ record.1 }}{% if prefix.vrf %}&vrf={{ prefix.vrf.pk }}{% endif %}{% if prefix.tenant %}&tenant={{ prefix.tenant.pk }}{% endif %}" class="btn btn-xs btn-success">{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available</a>
  72. {% else %}
  73. {% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available
  74. {% endif %}
  75. """
  76. IPADDRESS_ASSIGN_LINK = """
  77. {% if request.GET %}
  78. <a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?interface={{ request.GET.interface }}&return_url={{ request.GET.return_url }}">{{ record }}</a>
  79. {% else %}
  80. <a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?interface={{ record.interface.pk }}&return_url={{ request.path }}">{{ record }}</a>
  81. {% endif %}
  82. """
  83. IPADDRESS_PARENT = """
  84. {% if record.interface %}
  85. <a href="{{ record.interface.parent.get_absolute_url }}">{{ record.interface.parent }}</a>
  86. {% else %}
  87. &mdash;
  88. {% endif %}
  89. """
  90. VRF_LINK = """
  91. {% if record.vrf %}
  92. <a href="{{ record.vrf.get_absolute_url }}">{{ record.vrf }}</a>
  93. {% elif prefix.vrf %}
  94. {{ prefix.vrf }}
  95. {% else %}
  96. Global
  97. {% endif %}
  98. """
  99. STATUS_LABEL = """
  100. {% if record.pk %}
  101. <span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
  102. {% else %}
  103. <span class="label label-success">Available</span>
  104. {% endif %}
  105. """
  106. VLAN_LINK = """
  107. {% if record.pk %}
  108. <a href="{{ record.get_absolute_url }}">{{ record.vid }}</a>
  109. {% elif perms.ipam.add_vlan %}
  110. <a href="{% url 'ipam:vlan_add' %}?vid={{ record.vid }}&group={{ vlan_group.pk }}{% if vlan_group.site %}&site={{ vlan_group.site.pk }}{% endif %}" class="btn btn-xs btn-success">{{ record.available }} VLAN{{ record.available|pluralize }} available</a>
  111. {% else %}
  112. {{ record.available }} VLAN{{ record.available|pluralize }} available
  113. {% endif %}
  114. """
  115. VLAN_PREFIXES = """
  116. {% for prefix in record.prefixes.all %}
  117. <a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a>{% if not forloop.last %}<br />{% endif %}
  118. {% empty %}
  119. &mdash;
  120. {% endfor %}
  121. """
  122. VLAN_ROLE_LINK = """
  123. {% if record.role %}
  124. <a href="{% url 'ipam:vlan_list' %}?role={{ record.role.slug }}">{{ record.role }}</a>
  125. {% else %}
  126. &mdash;
  127. {% endif %}
  128. """
  129. VLANGROUP_ACTIONS = """
  130. <a href="{% url 'ipam:vlangroup_changelog' pk=record.pk %}" class="btn btn-default btn-xs" title="Change log">
  131. <i class="fa fa-history"></i>
  132. </a>
  133. {% with next_vid=record.get_next_available_vid %}
  134. {% if next_vid and perms.ipam.add_vlan %}
  135. <a href="{% url 'ipam:vlan_add' %}?site={{ record.site_id }}&group={{ record.pk }}&vid={{ next_vid }}" title="Add VLAN" class="btn btn-xs btn-success">
  136. <i class="glyphicon glyphicon-plus" aria-hidden="true"></i>
  137. </a>
  138. {% endif %}
  139. {% endwith %}
  140. {% if perms.ipam.change_vlangroup %}
  141. <a href="{% url 'ipam:vlangroup_edit' pk=record.pk %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
  142. {% endif %}
  143. """
  144. VLAN_MEMBER_UNTAGGED = """
  145. {% if record.untagged_vlan_id == vlan.pk %}
  146. <i class="glyphicon glyphicon-ok">
  147. {% endif %}
  148. """
  149. VLAN_MEMBER_ACTIONS = """
  150. {% if perms.dcim.change_interface %}
  151. <a href="{% if record.device %}{% url 'dcim:interface_edit' pk=record.pk %}{% else %}{% url 'virtualization:interface_edit' pk=record.pk %}{% endif %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil"></i></a>
  152. {% endif %}
  153. """
  154. TENANT_LINK = """
  155. {% if record.tenant %}
  156. <a href="{% url 'tenancy:tenant' slug=record.tenant.slug %}" title="{{ record.tenant.description }}">{{ record.tenant }}</a>
  157. {% elif record.vrf.tenant %}
  158. <a href="{% url 'tenancy:tenant' slug=record.vrf.tenant.slug %}" title="{{ record.vrf.tenant.description }}">{{ record.vrf.tenant }}</a>*
  159. {% else %}
  160. &mdash;
  161. {% endif %}
  162. """
  163. #
  164. # VRFs
  165. #
  166. class VRFTable(BaseTable):
  167. pk = ToggleColumn()
  168. name = tables.LinkColumn()
  169. rd = tables.Column(
  170. verbose_name='RD'
  171. )
  172. tenant = tables.TemplateColumn(
  173. template_code=COL_TENANT
  174. )
  175. enforce_unique = BooleanColumn(
  176. verbose_name='Unique'
  177. )
  178. tags = TagColumn(
  179. url_name='ipam:vrf_list'
  180. )
  181. class Meta(BaseTable.Meta):
  182. model = VRF
  183. fields = ('pk', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'tags')
  184. default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
  185. #
  186. # RIRs
  187. #
  188. class RIRTable(BaseTable):
  189. pk = ToggleColumn()
  190. name = tables.LinkColumn()
  191. is_private = BooleanColumn(
  192. verbose_name='Private'
  193. )
  194. aggregate_count = tables.Column(
  195. verbose_name='Aggregates'
  196. )
  197. actions = tables.TemplateColumn(
  198. template_code=RIR_ACTIONS,
  199. attrs={'td': {'class': 'text-right noprint'}},
  200. verbose_name=''
  201. )
  202. class Meta(BaseTable.Meta):
  203. model = RIR
  204. fields = ('pk', 'name', 'slug', 'is_private', 'aggregate_count', 'description', 'actions')
  205. default_columns = ('pk', 'name', 'is_private', 'aggregate_count', 'description', 'actions')
  206. class RIRDetailTable(RIRTable):
  207. stats_total = tables.Column(
  208. accessor='stats.total',
  209. verbose_name='Total',
  210. footer=lambda table: sum(r.stats['total'] for r in table.data)
  211. )
  212. stats_active = tables.Column(
  213. accessor='stats.active',
  214. verbose_name='Active',
  215. footer=lambda table: sum(r.stats['active'] for r in table.data)
  216. )
  217. stats_reserved = tables.Column(
  218. accessor='stats.reserved',
  219. verbose_name='Reserved',
  220. footer=lambda table: sum(r.stats['reserved'] for r in table.data)
  221. )
  222. stats_deprecated = tables.Column(
  223. accessor='stats.deprecated',
  224. verbose_name='Deprecated',
  225. footer=lambda table: sum(r.stats['deprecated'] for r in table.data)
  226. )
  227. stats_available = tables.Column(
  228. accessor='stats.available',
  229. verbose_name='Available',
  230. footer=lambda table: sum(r.stats['available'] for r in table.data)
  231. )
  232. utilization = tables.TemplateColumn(
  233. template_code=RIR_UTILIZATION,
  234. verbose_name='Utilization'
  235. )
  236. class Meta(RIRTable.Meta):
  237. fields = (
  238. 'pk', 'name', 'slug', 'is_private', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved',
  239. 'stats_deprecated', 'stats_available', 'utilization', 'actions',
  240. )
  241. default_columns = (
  242. 'pk', 'name', 'is_private', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved',
  243. 'stats_deprecated', 'stats_available', 'utilization', 'actions',
  244. )
  245. #
  246. # Aggregates
  247. #
  248. class AggregateTable(BaseTable):
  249. pk = ToggleColumn()
  250. prefix = tables.LinkColumn(
  251. verbose_name='Aggregate'
  252. )
  253. date_added = tables.DateColumn(
  254. format="Y-m-d",
  255. verbose_name='Added'
  256. )
  257. class Meta(BaseTable.Meta):
  258. model = Aggregate
  259. fields = ('pk', 'prefix', 'rir', 'date_added', 'description')
  260. class AggregateDetailTable(AggregateTable):
  261. child_count = tables.Column(
  262. verbose_name='Prefixes'
  263. )
  264. utilization = tables.TemplateColumn(
  265. template_code=UTILIZATION_GRAPH,
  266. orderable=False
  267. )
  268. tags = TagColumn(
  269. url_name='ipam:aggregate_list'
  270. )
  271. class Meta(AggregateTable.Meta):
  272. fields = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description', 'tags')
  273. default_columns = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description')
  274. #
  275. # Roles
  276. #
  277. class RoleTable(BaseTable):
  278. pk = ToggleColumn()
  279. prefix_count = tables.TemplateColumn(
  280. accessor=Accessor('prefixes.count'),
  281. template_code=ROLE_PREFIX_COUNT,
  282. orderable=False,
  283. verbose_name='Prefixes'
  284. )
  285. vlan_count = tables.TemplateColumn(
  286. accessor=Accessor('vlans.count'),
  287. template_code=ROLE_VLAN_COUNT,
  288. orderable=False,
  289. verbose_name='VLANs'
  290. )
  291. actions = tables.TemplateColumn(
  292. template_code=ROLE_ACTIONS,
  293. attrs={'td': {'class': 'text-right noprint'}},
  294. verbose_name=''
  295. )
  296. class Meta(BaseTable.Meta):
  297. model = Role
  298. fields = ('pk', 'name', 'slug', 'prefix_count', 'vlan_count', 'description', 'weight', 'actions')
  299. default_columns = ('pk', 'name', 'prefix_count', 'vlan_count', 'description', 'actions')
  300. #
  301. # Prefixes
  302. #
  303. class PrefixTable(BaseTable):
  304. pk = ToggleColumn()
  305. prefix = tables.TemplateColumn(
  306. template_code=PREFIX_LINK,
  307. attrs={'th': {'style': 'padding-left: 17px'}}
  308. )
  309. status = tables.TemplateColumn(
  310. template_code=STATUS_LABEL
  311. )
  312. vrf = tables.TemplateColumn(
  313. template_code=VRF_LINK,
  314. verbose_name='VRF'
  315. )
  316. tenant = tables.TemplateColumn(
  317. template_code=TENANT_LINK
  318. )
  319. site = tables.LinkColumn(
  320. viewname='dcim:site',
  321. args=[Accessor('site.slug')]
  322. )
  323. vlan = tables.LinkColumn(
  324. viewname='ipam:vlan',
  325. args=[Accessor('vlan.pk')],
  326. verbose_name='VLAN'
  327. )
  328. role = tables.TemplateColumn(
  329. template_code=PREFIX_ROLE_LINK
  330. )
  331. is_pool = BooleanColumn(
  332. verbose_name='Pool'
  333. )
  334. class Meta(BaseTable.Meta):
  335. model = Prefix
  336. fields = ('pk', 'prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'is_pool', 'description')
  337. default_columns = ('pk', 'prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'description')
  338. row_attrs = {
  339. 'class': lambda record: 'success' if not record.pk else '',
  340. }
  341. class PrefixDetailTable(PrefixTable):
  342. utilization = tables.TemplateColumn(
  343. template_code=UTILIZATION_GRAPH,
  344. orderable=False
  345. )
  346. tenant = tables.TemplateColumn(
  347. template_code=COL_TENANT
  348. )
  349. tags = TagColumn(
  350. url_name='ipam:prefix_list'
  351. )
  352. class Meta(PrefixTable.Meta):
  353. fields = (
  354. 'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'is_pool', 'description',
  355. 'tags',
  356. )
  357. default_columns = (
  358. 'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
  359. )
  360. #
  361. # IPAddresses
  362. #
  363. class IPAddressTable(BaseTable):
  364. pk = ToggleColumn()
  365. address = tables.TemplateColumn(
  366. template_code=IPADDRESS_LINK,
  367. verbose_name='IP Address'
  368. )
  369. vrf = tables.TemplateColumn(
  370. template_code=VRF_LINK,
  371. verbose_name='VRF'
  372. )
  373. status = tables.TemplateColumn(
  374. template_code=STATUS_LABEL
  375. )
  376. tenant = tables.TemplateColumn(
  377. template_code=TENANT_LINK
  378. )
  379. parent = tables.TemplateColumn(
  380. template_code=IPADDRESS_PARENT,
  381. orderable=False
  382. )
  383. interface = tables.Column(
  384. orderable=False
  385. )
  386. class Meta(BaseTable.Meta):
  387. model = IPAddress
  388. fields = (
  389. 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'dns_name', 'description',
  390. )
  391. row_attrs = {
  392. 'class': lambda record: 'success' if not isinstance(record, IPAddress) else '',
  393. }
  394. class IPAddressDetailTable(IPAddressTable):
  395. nat_inside = tables.LinkColumn(
  396. viewname='ipam:ipaddress',
  397. args=[Accessor('nat_inside.pk')],
  398. orderable=False,
  399. verbose_name='NAT (Inside)'
  400. )
  401. tenant = tables.TemplateColumn(
  402. template_code=COL_TENANT
  403. )
  404. tags = TagColumn(
  405. url_name='ipam:ipaddress_list'
  406. )
  407. class Meta(IPAddressTable.Meta):
  408. fields = (
  409. 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'parent', 'interface', 'dns_name',
  410. 'description', 'tags',
  411. )
  412. default_columns = (
  413. 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'dns_name', 'description',
  414. )
  415. class IPAddressAssignTable(BaseTable):
  416. address = tables.TemplateColumn(
  417. template_code=IPADDRESS_ASSIGN_LINK,
  418. verbose_name='IP Address'
  419. )
  420. status = tables.TemplateColumn(
  421. template_code=STATUS_LABEL
  422. )
  423. parent = tables.TemplateColumn(
  424. template_code=IPADDRESS_PARENT,
  425. orderable=False
  426. )
  427. interface = tables.Column(
  428. orderable=False
  429. )
  430. class Meta(BaseTable.Meta):
  431. model = IPAddress
  432. fields = ('address', 'dns_name', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'description')
  433. orderable = False
  434. class InterfaceIPAddressTable(BaseTable):
  435. """
  436. List IP addresses assigned to a specific Interface.
  437. """
  438. address = tables.LinkColumn(
  439. verbose_name='IP Address'
  440. )
  441. vrf = tables.TemplateColumn(
  442. template_code=VRF_LINK,
  443. verbose_name='VRF'
  444. )
  445. status = tables.TemplateColumn(
  446. template_code=STATUS_LABEL
  447. )
  448. tenant = tables.TemplateColumn(
  449. template_code=TENANT_LINK
  450. )
  451. class Meta(BaseTable.Meta):
  452. model = IPAddress
  453. fields = ('address', 'vrf', 'status', 'role', 'tenant', 'description')
  454. #
  455. # VLAN groups
  456. #
  457. class VLANGroupTable(BaseTable):
  458. pk = ToggleColumn()
  459. name = tables.LinkColumn()
  460. site = tables.LinkColumn(
  461. viewname='dcim:site',
  462. args=[Accessor('site.slug')]
  463. )
  464. vlan_count = tables.Column(
  465. verbose_name='VLANs'
  466. )
  467. actions = tables.TemplateColumn(
  468. template_code=VLANGROUP_ACTIONS,
  469. attrs={'td': {'class': 'text-right noprint'}},
  470. verbose_name=''
  471. )
  472. class Meta(BaseTable.Meta):
  473. model = VLANGroup
  474. fields = ('pk', 'name', 'site', 'vlan_count', 'slug', 'description', 'actions')
  475. default_columns = ('pk', 'name', 'site', 'vlan_count', 'description', 'actions')
  476. #
  477. # VLANs
  478. #
  479. class VLANTable(BaseTable):
  480. pk = ToggleColumn()
  481. vid = tables.TemplateColumn(
  482. template_code=VLAN_LINK,
  483. verbose_name='ID'
  484. )
  485. site = tables.LinkColumn(
  486. viewname='dcim:site',
  487. args=[Accessor('site.slug')]
  488. )
  489. group = tables.LinkColumn(
  490. viewname='ipam:vlangroup_vlans',
  491. args=[Accessor('group.pk')]
  492. )
  493. tenant = tables.TemplateColumn(
  494. template_code=COL_TENANT
  495. )
  496. status = tables.TemplateColumn(
  497. template_code=STATUS_LABEL
  498. )
  499. role = tables.TemplateColumn(
  500. template_code=VLAN_ROLE_LINK
  501. )
  502. class Meta(BaseTable.Meta):
  503. model = VLAN
  504. fields = ('pk', 'vid', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
  505. row_attrs = {
  506. 'class': lambda record: 'success' if not isinstance(record, VLAN) else '',
  507. }
  508. class VLANDetailTable(VLANTable):
  509. prefixes = tables.TemplateColumn(
  510. template_code=VLAN_PREFIXES,
  511. orderable=False,
  512. verbose_name='Prefixes'
  513. )
  514. tenant = tables.TemplateColumn(
  515. template_code=COL_TENANT
  516. )
  517. tags = TagColumn(
  518. url_name='ipam:vlan_list'
  519. )
  520. class Meta(VLANTable.Meta):
  521. fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description', 'tags')
  522. default_columns = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
  523. class VLANMemberTable(BaseTable):
  524. parent = tables.LinkColumn(
  525. order_by=['device', 'virtual_machine']
  526. )
  527. name = tables.LinkColumn(
  528. verbose_name='Interface'
  529. )
  530. untagged = tables.TemplateColumn(
  531. template_code=VLAN_MEMBER_UNTAGGED,
  532. orderable=False
  533. )
  534. actions = tables.TemplateColumn(
  535. template_code=VLAN_MEMBER_ACTIONS,
  536. attrs={'td': {'class': 'text-right noprint'}},
  537. verbose_name=''
  538. )
  539. class Meta(BaseTable.Meta):
  540. model = Interface
  541. fields = ('parent', 'name', 'untagged', 'actions')
  542. class InterfaceVLANTable(BaseTable):
  543. """
  544. List VLANs assigned to a specific Interface.
  545. """
  546. vid = tables.LinkColumn(
  547. viewname='ipam:vlan',
  548. args=[Accessor('pk')],
  549. verbose_name='ID'
  550. )
  551. tagged = BooleanColumn()
  552. site = tables.LinkColumn(
  553. viewname='dcim:site',
  554. args=[Accessor('site.slug')]
  555. )
  556. group = tables.Column(
  557. accessor=Accessor('group.name'),
  558. verbose_name='Group'
  559. )
  560. tenant = tables.TemplateColumn(
  561. template_code=COL_TENANT
  562. )
  563. status = tables.TemplateColumn(
  564. template_code=STATUS_LABEL
  565. )
  566. role = tables.TemplateColumn(
  567. template_code=VLAN_ROLE_LINK
  568. )
  569. class Meta(BaseTable.Meta):
  570. model = VLAN
  571. fields = ('vid', 'tagged', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
  572. def __init__(self, interface, *args, **kwargs):
  573. self.interface = interface
  574. super().__init__(*args, **kwargs)
  575. #
  576. # Services
  577. #
  578. class ServiceTable(BaseTable):
  579. pk = ToggleColumn()
  580. name = tables.LinkColumn(
  581. viewname='ipam:service',
  582. args=[Accessor('pk')]
  583. )
  584. tags = TagColumn(
  585. url_name='ipam:service_list'
  586. )
  587. class Meta(BaseTable.Meta):
  588. model = Service
  589. fields = ('pk', 'name', 'parent', 'protocol', 'port', 'ipaddresses', 'description', 'tags')
  590. default_columns = ('pk', 'name', 'parent', 'protocol', 'port', 'description')