tables.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. from __future__ import unicode_literals
  2. import django_tables2 as tables
  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, ToggleColumn
  7. from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
  8. RIR_UTILIZATION = """
  9. <div class="progress">
  10. {% if record.stats.total %}
  11. <div class="progress-bar" role="progressbar" style="width: {{ record.stats.percentages.active }}%;">
  12. <span class="sr-only">{{ record.stats.percentages.active }}%</span>
  13. </div>
  14. <div class="progress-bar progress-bar-info" role="progressbar" style="width: {{ record.stats.percentages.reserved }}%;">
  15. <span class="sr-only">{{ record.stats.percentages.reserved }}%</span>
  16. </div>
  17. <div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{ record.stats.percentages.deprecated }}%;">
  18. <span class="sr-only">{{ record.stats.percentages.deprecated }}%</span>
  19. </div>
  20. <div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ record.stats.percentages.available }}%;">
  21. <span class="sr-only">{{ record.stats.percentages.available }}%</span>
  22. </div>
  23. {% endif %}
  24. </div>
  25. """
  26. RIR_ACTIONS = """
  27. <a href="{% url 'ipam:rir_changelog' slug=record.slug %}" class="btn btn-default btn-xs" title="Changelog">
  28. <i class="fa fa-history"></i>
  29. </a>
  30. {% if perms.ipam.change_rir %}
  31. <a href="{% url 'ipam:rir_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
  32. {% endif %}
  33. """
  34. UTILIZATION_GRAPH = """
  35. {% load helpers %}
  36. {% if record.pk %}{% utilization_graph record.get_utilization %}{% else %}&mdash;{% endif %}
  37. """
  38. ROLE_PREFIX_COUNT = """
  39. <a href="{% url 'ipam:prefix_list' %}?role={{ record.slug }}">{{ value }}</a>
  40. """
  41. ROLE_VLAN_COUNT = """
  42. <a href="{% url 'ipam:vlan_list' %}?role={{ record.slug }}">{{ value }}</a>
  43. """
  44. ROLE_ACTIONS = """
  45. <a href="{% url 'ipam:role_changelog' slug=record.slug %}" class="btn btn-default btn-xs" title="Changelog">
  46. <i class="fa fa-history"></i>
  47. </a>
  48. {% if perms.ipam.change_role %}
  49. <a href="{% url 'ipam:role_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
  50. {% endif %}
  51. """
  52. PREFIX_LINK = """
  53. {% if record.has_children %}
  54. <span class="text-nowrap" style="padding-left: {{ record.depth }}0px "><i class="fa fa-caret-right"></i></a>
  55. {% else %}
  56. <span class="text-nowrap" style="padding-left: {{ record.depth }}9px">
  57. {% endif %}
  58. <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>
  59. </span>
  60. """
  61. PREFIX_ROLE_LINK = """
  62. {% if record.role %}
  63. <a href="{% url 'ipam:prefix_list' %}?role={{ record.role.slug }}">{{ record.role }}</a>
  64. {% else %}
  65. &mdash;
  66. {% endif %}
  67. """
  68. IPADDRESS_LINK = """
  69. {% if record.pk %}
  70. <a href="{{ record.get_absolute_url }}">{{ record.address }}</a>
  71. {% elif perms.ipam.add_ipaddress %}
  72. <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>
  73. {% else %}
  74. {% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available
  75. {% endif %}
  76. """
  77. IPADDRESS_ASSIGN_LINK = """
  78. <a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?interface={{ request.GET.interface }}&return_url={{ request.GET.return_url }}">{{ record }}</a>
  79. """
  80. IPADDRESS_PARENT = """
  81. {% if record.interface %}
  82. <a href="{{ record.interface.parent.get_absolute_url }}">{{ record.interface.parent }}</a>
  83. {% else %}
  84. &mdash;
  85. {% endif %}
  86. """
  87. VRF_LINK = """
  88. {% if record.vrf %}
  89. <a href="{{ record.vrf.get_absolute_url }}">{{ record.vrf }}</a>
  90. {% elif prefix.vrf %}
  91. {{ prefix.vrf }}
  92. {% else %}
  93. Global
  94. {% endif %}
  95. """
  96. STATUS_LABEL = """
  97. {% if record.pk %}
  98. <span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
  99. {% else %}
  100. <span class="label label-success">Available</span>
  101. {% endif %}
  102. """
  103. VLAN_PREFIXES = """
  104. {% for prefix in record.prefixes.all %}
  105. <a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a>{% if not forloop.last %}<br />{% endif %}
  106. {% empty %}
  107. &mdash;
  108. {% endfor %}
  109. """
  110. VLAN_ROLE_LINK = """
  111. {% if record.role %}
  112. <a href="{% url 'ipam:vlan_list' %}?role={{ record.role.slug }}">{{ record.role }}</a>
  113. {% else %}
  114. &mdash;
  115. {% endif %}
  116. """
  117. VLANGROUP_ACTIONS = """
  118. <a href="{% url 'ipam:vlangroup_changelog' pk=record.pk %}" class="btn btn-default btn-xs" title="Changelog">
  119. <i class="fa fa-history"></i>
  120. </a>
  121. {% with next_vid=record.get_next_available_vid %}
  122. {% if next_vid and perms.ipam.add_vlan %}
  123. <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">
  124. <i class="glyphicon glyphicon-plus" aria-hidden="true"></i>
  125. </a>
  126. {% endif %}
  127. {% endwith %}
  128. {% if perms.ipam.change_vlangroup %}
  129. <a href="{% url 'ipam:vlangroup_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
  130. {% endif %}
  131. """
  132. VLAN_MEMBER_UNTAGGED = """
  133. {% if record.untagged_vlan_id == vlan.pk %}
  134. <i class="glyphicon glyphicon-ok">
  135. {% endif %}
  136. """
  137. VLAN_MEMBER_ACTIONS = """
  138. {% if perms.dcim.change_interface %}
  139. <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>
  140. {% endif %}
  141. """
  142. TENANT_LINK = """
  143. {% if record.tenant %}
  144. <a href="{% url 'tenancy:tenant' slug=record.tenant.slug %}" title="{{ record.tenant.description }}">{{ record.tenant }}</a>
  145. {% elif record.vrf.tenant %}
  146. <a href="{% url 'tenancy:tenant' slug=record.vrf.tenant.slug %}" title="{{ record.vrf.tenant.description }}">{{ record.vrf.tenant }}</a>*
  147. {% else %}
  148. &mdash;
  149. {% endif %}
  150. """
  151. #
  152. # VRFs
  153. #
  154. class VRFTable(BaseTable):
  155. pk = ToggleColumn()
  156. name = tables.LinkColumn()
  157. rd = tables.Column(verbose_name='RD')
  158. tenant = tables.TemplateColumn(template_code=COL_TENANT)
  159. class Meta(BaseTable.Meta):
  160. model = VRF
  161. fields = ('pk', 'name', 'rd', 'tenant', 'description')
  162. #
  163. # RIRs
  164. #
  165. class RIRTable(BaseTable):
  166. pk = ToggleColumn()
  167. name = tables.LinkColumn(verbose_name='Name')
  168. is_private = BooleanColumn(verbose_name='Private')
  169. aggregate_count = tables.Column(verbose_name='Aggregates')
  170. actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
  171. class Meta(BaseTable.Meta):
  172. model = RIR
  173. fields = ('pk', 'name', 'is_private', 'aggregate_count', 'actions')
  174. class RIRDetailTable(RIRTable):
  175. stats_total = tables.Column(accessor='stats.total', verbose_name='Total',
  176. footer=lambda table: sum(r.stats['total'] for r in table.data))
  177. stats_active = tables.Column(accessor='stats.active', verbose_name='Active',
  178. footer=lambda table: sum(r.stats['active'] for r in table.data))
  179. stats_reserved = tables.Column(accessor='stats.reserved', verbose_name='Reserved',
  180. footer=lambda table: sum(r.stats['reserved'] for r in table.data))
  181. stats_deprecated = tables.Column(accessor='stats.deprecated', verbose_name='Deprecated',
  182. footer=lambda table: sum(r.stats['deprecated'] for r in table.data))
  183. stats_available = tables.Column(accessor='stats.available', verbose_name='Available',
  184. footer=lambda table: sum(r.stats['available'] for r in table.data))
  185. utilization = tables.TemplateColumn(template_code=RIR_UTILIZATION, verbose_name='Utilization')
  186. class Meta(RIRTable.Meta):
  187. fields = (
  188. 'pk', 'name', 'is_private', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved',
  189. 'stats_deprecated', 'stats_available', 'utilization', 'actions',
  190. )
  191. #
  192. # Aggregates
  193. #
  194. class AggregateTable(BaseTable):
  195. pk = ToggleColumn()
  196. prefix = tables.LinkColumn(verbose_name='Aggregate')
  197. date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added')
  198. class Meta(BaseTable.Meta):
  199. model = Aggregate
  200. fields = ('pk', 'prefix', 'rir', 'date_added', 'description')
  201. class AggregateDetailTable(AggregateTable):
  202. child_count = tables.Column(verbose_name='Prefixes')
  203. utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
  204. class Meta(AggregateTable.Meta):
  205. fields = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description')
  206. #
  207. # Roles
  208. #
  209. class RoleTable(BaseTable):
  210. pk = ToggleColumn()
  211. prefix_count = tables.TemplateColumn(
  212. accessor=Accessor('prefixes.count'),
  213. template_code=ROLE_PREFIX_COUNT,
  214. orderable=False,
  215. verbose_name='Prefixes'
  216. )
  217. vlan_count = tables.TemplateColumn(
  218. accessor=Accessor('vlans.count'),
  219. template_code=ROLE_VLAN_COUNT,
  220. orderable=False,
  221. verbose_name='VLANs'
  222. )
  223. actions = tables.TemplateColumn(template_code=ROLE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
  224. class Meta(BaseTable.Meta):
  225. model = Role
  226. fields = ('pk', 'name', 'prefix_count', 'vlan_count', 'slug', 'actions')
  227. #
  228. # Prefixes
  229. #
  230. class PrefixTable(BaseTable):
  231. pk = ToggleColumn()
  232. prefix = tables.TemplateColumn(PREFIX_LINK, attrs={'th': {'style': 'padding-left: 17px'}})
  233. status = tables.TemplateColumn(STATUS_LABEL)
  234. vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
  235. tenant = tables.TemplateColumn(template_code=TENANT_LINK)
  236. site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
  237. vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN')
  238. role = tables.TemplateColumn(PREFIX_ROLE_LINK)
  239. class Meta(BaseTable.Meta):
  240. model = Prefix
  241. fields = ('pk', 'prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'description')
  242. row_attrs = {
  243. 'class': lambda record: 'success' if not record.pk else '',
  244. }
  245. class PrefixDetailTable(PrefixTable):
  246. utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False)
  247. class Meta(PrefixTable.Meta):
  248. fields = ('pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description')
  249. #
  250. # IPAddresses
  251. #
  252. class IPAddressTable(BaseTable):
  253. pk = ToggleColumn()
  254. address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address')
  255. vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
  256. status = tables.TemplateColumn(STATUS_LABEL)
  257. tenant = tables.TemplateColumn(template_code=TENANT_LINK)
  258. parent = tables.TemplateColumn(IPADDRESS_PARENT, orderable=False)
  259. interface = tables.Column(orderable=False)
  260. class Meta(BaseTable.Meta):
  261. model = IPAddress
  262. fields = ('pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'description')
  263. row_attrs = {
  264. 'class': lambda record: 'success' if not isinstance(record, IPAddress) else '',
  265. }
  266. class IPAddressDetailTable(IPAddressTable):
  267. nat_inside = tables.LinkColumn(
  268. 'ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False, verbose_name='NAT (Inside)'
  269. )
  270. class Meta(IPAddressTable.Meta):
  271. fields = (
  272. 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'parent', 'interface', 'description',
  273. )
  274. class IPAddressAssignTable(BaseTable):
  275. address = tables.TemplateColumn(IPADDRESS_ASSIGN_LINK, verbose_name='IP Address')
  276. status = tables.TemplateColumn(STATUS_LABEL)
  277. parent = tables.TemplateColumn(IPADDRESS_PARENT, orderable=False)
  278. interface = tables.Column(orderable=False)
  279. class Meta(BaseTable.Meta):
  280. model = IPAddress
  281. fields = ('address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'description')
  282. orderable = False
  283. class InterfaceIPAddressTable(BaseTable):
  284. """
  285. List IP addresses assigned to a specific Interface.
  286. """
  287. address = tables.TemplateColumn(IPADDRESS_ASSIGN_LINK, verbose_name='IP Address')
  288. vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
  289. status = tables.TemplateColumn(STATUS_LABEL)
  290. tenant = tables.TemplateColumn(template_code=TENANT_LINK)
  291. class Meta(BaseTable.Meta):
  292. model = IPAddress
  293. fields = ('address', 'vrf', 'status', 'role', 'tenant', 'description')
  294. #
  295. # VLAN groups
  296. #
  297. class VLANGroupTable(BaseTable):
  298. pk = ToggleColumn()
  299. name = tables.LinkColumn(verbose_name='Name')
  300. site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
  301. vlan_count = tables.Column(verbose_name='VLANs')
  302. slug = tables.Column(verbose_name='Slug')
  303. actions = tables.TemplateColumn(template_code=VLANGROUP_ACTIONS, attrs={'td': {'class': 'text-right'}},
  304. verbose_name='')
  305. class Meta(BaseTable.Meta):
  306. model = VLANGroup
  307. fields = ('pk', 'name', 'site', 'vlan_count', 'slug', 'actions')
  308. #
  309. # VLANs
  310. #
  311. class VLANTable(BaseTable):
  312. pk = ToggleColumn()
  313. vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
  314. site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
  315. group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
  316. tenant = tables.TemplateColumn(template_code=COL_TENANT)
  317. status = tables.TemplateColumn(STATUS_LABEL)
  318. role = tables.TemplateColumn(VLAN_ROLE_LINK)
  319. class Meta(BaseTable.Meta):
  320. model = VLAN
  321. fields = ('pk', 'vid', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
  322. class VLANDetailTable(VLANTable):
  323. prefixes = tables.TemplateColumn(VLAN_PREFIXES, orderable=False, verbose_name='Prefixes')
  324. class Meta(VLANTable.Meta):
  325. fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
  326. class VLANMemberTable(BaseTable):
  327. parent = tables.LinkColumn(order_by=['device', 'virtual_machine'])
  328. name = tables.Column(verbose_name='Interface')
  329. untagged = tables.TemplateColumn(
  330. template_code=VLAN_MEMBER_UNTAGGED,
  331. orderable=False
  332. )
  333. actions = tables.TemplateColumn(
  334. template_code=VLAN_MEMBER_ACTIONS,
  335. attrs={'td': {'class': 'text-right'}},
  336. verbose_name=''
  337. )
  338. class Meta(BaseTable.Meta):
  339. model = Interface
  340. fields = ('parent', 'name', 'untagged', 'actions')
  341. class InterfaceVLANTable(BaseTable):
  342. """
  343. List VLANs assigned to a specific Interface.
  344. """
  345. vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
  346. tagged = BooleanColumn()
  347. site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
  348. group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
  349. tenant = tables.TemplateColumn(template_code=COL_TENANT)
  350. status = tables.TemplateColumn(STATUS_LABEL)
  351. role = tables.TemplateColumn(VLAN_ROLE_LINK)
  352. class Meta(BaseTable.Meta):
  353. model = VLAN
  354. fields = ('vid', 'tagged', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
  355. def __init__(self, interface, *args, **kwargs):
  356. self.interface = interface
  357. super(InterfaceVLANTable, self).__init__(*args, **kwargs)
  358. #
  359. # Services
  360. #
  361. class ServiceTable(BaseTable):
  362. pk = ToggleColumn()
  363. name = tables.LinkColumn(
  364. viewname='ipam:service',
  365. args=[Accessor('pk')]
  366. )
  367. class Meta(BaseTable.Meta):
  368. model = Service
  369. fields = ('pk', 'name', 'parent', 'protocol', 'port', 'description')