views.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. from django.conf import settings
  2. from django.contrib import messages
  3. from django.contrib.auth.decorators import permission_required
  4. from django.contrib.auth.mixins import PermissionRequiredMixin
  5. from django.db import transaction
  6. from django.db.models import Count, OuterRef, Subquery
  7. from django.shortcuts import get_object_or_404, redirect, render
  8. from django.views.generic import View
  9. from django_tables2 import RequestConfig
  10. from extras.models import Graph
  11. from utilities.forms import ConfirmationForm
  12. from utilities.paginator import EnhancedPaginator
  13. from utilities.views import (
  14. BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
  15. )
  16. from . import filters, forms, tables
  17. from .choices import CircuitTerminationSideChoices
  18. from .models import Circuit, CircuitTermination, CircuitType, Provider
  19. #
  20. # Providers
  21. #
  22. class ProviderListView(PermissionRequiredMixin, ObjectListView):
  23. permission_required = 'circuits.view_provider'
  24. queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
  25. filterset = filters.ProviderFilterSet
  26. filterset_form = forms.ProviderFilterForm
  27. table = tables.ProviderDetailTable
  28. template_name = 'circuits/provider_list.html'
  29. class ProviderView(PermissionRequiredMixin, View):
  30. permission_required = 'circuits.view_provider'
  31. def get(self, request, slug):
  32. provider = get_object_or_404(Provider, slug=slug)
  33. circuits = Circuit.objects.filter(provider=provider).prefetch_related('type', 'tenant', 'terminations__site')
  34. show_graphs = Graph.objects.filter(type__model='provider').exists()
  35. circuits_table = tables.CircuitTable(circuits, orderable=False)
  36. circuits_table.columns.hide('provider')
  37. paginate = {
  38. 'paginator_class': EnhancedPaginator,
  39. 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
  40. }
  41. RequestConfig(request, paginate).configure(circuits_table)
  42. return render(request, 'circuits/provider.html', {
  43. 'provider': provider,
  44. 'circuits_table': circuits_table,
  45. 'show_graphs': show_graphs,
  46. })
  47. class ProviderCreateView(PermissionRequiredMixin, ObjectEditView):
  48. permission_required = 'circuits.add_provider'
  49. model = Provider
  50. model_form = forms.ProviderForm
  51. template_name = 'circuits/provider_edit.html'
  52. default_return_url = 'circuits:provider_list'
  53. class ProviderEditView(ProviderCreateView):
  54. permission_required = 'circuits.change_provider'
  55. class ProviderDeleteView(PermissionRequiredMixin, ObjectDeleteView):
  56. permission_required = 'circuits.delete_provider'
  57. model = Provider
  58. default_return_url = 'circuits:provider_list'
  59. class ProviderBulkImportView(PermissionRequiredMixin, BulkImportView):
  60. permission_required = 'circuits.add_provider'
  61. model_form = forms.ProviderCSVForm
  62. table = tables.ProviderTable
  63. default_return_url = 'circuits:provider_list'
  64. class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
  65. permission_required = 'circuits.change_provider'
  66. queryset = Provider.objects.all()
  67. filterset = filters.ProviderFilterSet
  68. table = tables.ProviderTable
  69. form = forms.ProviderBulkEditForm
  70. default_return_url = 'circuits:provider_list'
  71. class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
  72. permission_required = 'circuits.delete_provider'
  73. queryset = Provider.objects.all()
  74. filterset = filters.ProviderFilterSet
  75. table = tables.ProviderTable
  76. default_return_url = 'circuits:provider_list'
  77. #
  78. # Circuit Types
  79. #
  80. class CircuitTypeListView(PermissionRequiredMixin, ObjectListView):
  81. permission_required = 'circuits.view_circuittype'
  82. queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
  83. table = tables.CircuitTypeTable
  84. template_name = 'circuits/circuittype_list.html'
  85. class CircuitTypeCreateView(PermissionRequiredMixin, ObjectEditView):
  86. permission_required = 'circuits.add_circuittype'
  87. model = CircuitType
  88. model_form = forms.CircuitTypeForm
  89. default_return_url = 'circuits:circuittype_list'
  90. class CircuitTypeEditView(CircuitTypeCreateView):
  91. permission_required = 'circuits.change_circuittype'
  92. class CircuitTypeBulkImportView(PermissionRequiredMixin, BulkImportView):
  93. permission_required = 'circuits.add_circuittype'
  94. model_form = forms.CircuitTypeCSVForm
  95. table = tables.CircuitTypeTable
  96. default_return_url = 'circuits:circuittype_list'
  97. class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
  98. permission_required = 'circuits.delete_circuittype'
  99. queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
  100. table = tables.CircuitTypeTable
  101. default_return_url = 'circuits:circuittype_list'
  102. #
  103. # Circuits
  104. #
  105. class CircuitListView(PermissionRequiredMixin, ObjectListView):
  106. permission_required = 'circuits.view_circuit'
  107. _terminations = CircuitTermination.objects.filter(circuit=OuterRef('pk'))
  108. queryset = Circuit.objects.prefetch_related(
  109. 'provider', 'type', 'tenant', 'terminations__site'
  110. ).annotate(
  111. a_side=Subquery(_terminations.filter(term_side='A').values('site__name')[:1]),
  112. z_side=Subquery(_terminations.filter(term_side='Z').values('site__name')[:1]),
  113. )
  114. filterset = filters.CircuitFilterSet
  115. filterset_form = forms.CircuitFilterForm
  116. table = tables.CircuitTable
  117. template_name = 'circuits/circuit_list.html'
  118. class CircuitView(PermissionRequiredMixin, View):
  119. permission_required = 'circuits.view_circuit'
  120. def get(self, request, pk):
  121. circuit = get_object_or_404(Circuit.objects.prefetch_related('provider', 'type', 'tenant__group'), pk=pk)
  122. termination_a = CircuitTermination.objects.prefetch_related(
  123. 'site__region', 'connected_endpoint__device'
  124. ).filter(
  125. circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A
  126. ).first()
  127. termination_z = CircuitTermination.objects.prefetch_related(
  128. 'site__region', 'connected_endpoint__device'
  129. ).filter(
  130. circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_Z
  131. ).first()
  132. return render(request, 'circuits/circuit.html', {
  133. 'circuit': circuit,
  134. 'termination_a': termination_a,
  135. 'termination_z': termination_z,
  136. })
  137. class CircuitCreateView(PermissionRequiredMixin, ObjectEditView):
  138. permission_required = 'circuits.add_circuit'
  139. model = Circuit
  140. model_form = forms.CircuitForm
  141. template_name = 'circuits/circuit_edit.html'
  142. default_return_url = 'circuits:circuit_list'
  143. class CircuitEditView(CircuitCreateView):
  144. permission_required = 'circuits.change_circuit'
  145. class CircuitDeleteView(PermissionRequiredMixin, ObjectDeleteView):
  146. permission_required = 'circuits.delete_circuit'
  147. model = Circuit
  148. default_return_url = 'circuits:circuit_list'
  149. class CircuitBulkImportView(PermissionRequiredMixin, BulkImportView):
  150. permission_required = 'circuits.add_circuit'
  151. model_form = forms.CircuitCSVForm
  152. table = tables.CircuitTable
  153. default_return_url = 'circuits:circuit_list'
  154. class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
  155. permission_required = 'circuits.change_circuit'
  156. queryset = Circuit.objects.prefetch_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
  157. filterset = filters.CircuitFilterSet
  158. table = tables.CircuitTable
  159. form = forms.CircuitBulkEditForm
  160. default_return_url = 'circuits:circuit_list'
  161. class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
  162. permission_required = 'circuits.delete_circuit'
  163. queryset = Circuit.objects.prefetch_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
  164. filterset = filters.CircuitFilterSet
  165. table = tables.CircuitTable
  166. default_return_url = 'circuits:circuit_list'
  167. @permission_required('circuits.change_circuittermination')
  168. def circuit_terminations_swap(request, pk):
  169. circuit = get_object_or_404(Circuit, pk=pk)
  170. termination_a = CircuitTermination.objects.filter(
  171. circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A
  172. ).first()
  173. termination_z = CircuitTermination.objects.filter(
  174. circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_Z
  175. ).first()
  176. if not termination_a and not termination_z:
  177. messages.error(request, "No terminations have been defined for circuit {}.".format(circuit))
  178. return redirect('circuits:circuit', pk=circuit.pk)
  179. if request.method == 'POST':
  180. form = ConfirmationForm(request.POST)
  181. if form.is_valid():
  182. if termination_a and termination_z:
  183. # Use a placeholder to avoid an IntegrityError on the (circuit, term_side) unique constraint
  184. with transaction.atomic():
  185. termination_a.term_side = '_'
  186. termination_a.save()
  187. termination_z.term_side = 'A'
  188. termination_z.save()
  189. termination_a.term_side = 'Z'
  190. termination_a.save()
  191. elif termination_a:
  192. termination_a.term_side = 'Z'
  193. termination_a.save()
  194. else:
  195. termination_z.term_side = 'A'
  196. termination_z.save()
  197. messages.success(request, "Swapped terminations for circuit {}.".format(circuit))
  198. return redirect('circuits:circuit', pk=circuit.pk)
  199. else:
  200. form = ConfirmationForm()
  201. return render(request, 'circuits/circuit_terminations_swap.html', {
  202. 'circuit': circuit,
  203. 'termination_a': termination_a,
  204. 'termination_z': termination_z,
  205. 'form': form,
  206. 'panel_class': 'default',
  207. 'button_class': 'primary',
  208. 'return_url': circuit.get_absolute_url(),
  209. })
  210. #
  211. # Circuit terminations
  212. #
  213. class CircuitTerminationCreateView(PermissionRequiredMixin, ObjectEditView):
  214. permission_required = 'circuits.add_circuittermination'
  215. model = CircuitTermination
  216. model_form = forms.CircuitTerminationForm
  217. template_name = 'circuits/circuittermination_edit.html'
  218. def alter_obj(self, obj, request, url_args, url_kwargs):
  219. if 'circuit' in url_kwargs:
  220. obj.circuit = get_object_or_404(Circuit, pk=url_kwargs['circuit'])
  221. return obj
  222. def get_return_url(self, request, obj):
  223. return obj.circuit.get_absolute_url()
  224. class CircuitTerminationEditView(CircuitTerminationCreateView):
  225. permission_required = 'circuits.change_circuittermination'
  226. class CircuitTerminationDeleteView(PermissionRequiredMixin, ObjectDeleteView):
  227. permission_required = 'circuits.delete_circuittermination'
  228. model = CircuitTermination