views.py 9.2 KB

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