tables.py 20 KB

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