views.py 10 KB

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