querysets.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. from django.contrib.contenttypes.models import ContentType
  2. from django.db.models import Count, F, OuterRef, Q, Subquery, Value
  3. from django.db.models.expressions import RawSQL
  4. from django.db.models.functions import Round
  5. from utilities.querysets import RestrictedQuerySet
  6. from utilities.utils import count_related
  7. __all__ = (
  8. 'ASNRangeQuerySet',
  9. 'PrefixQuerySet',
  10. 'VLANQuerySet',
  11. )
  12. class ASNRangeQuerySet(RestrictedQuerySet):
  13. def annotate_asn_counts(self):
  14. """
  15. Annotate the number of ASNs which appear within each range.
  16. """
  17. from .models import ASN
  18. # Because ASN does not have a foreign key to ASNRange, we create a fake column "_" with a consistent value
  19. # that we can use to count ASNs and return a single value per ASNRange.
  20. asns = ASN.objects.filter(
  21. asn__gte=OuterRef('start'),
  22. asn__lte=OuterRef('end')
  23. ).order_by().annotate(_=Value(1)).values('_').annotate(c=Count('*')).values('c')
  24. return self.annotate(asn_count=Subquery(asns))
  25. class PrefixQuerySet(RestrictedQuerySet):
  26. def annotate_hierarchy(self):
  27. """
  28. Annotate the depth and number of child prefixes for each Prefix. Cast null VRF values to zero for
  29. comparison. (NULL != NULL).
  30. """
  31. return self.annotate(
  32. hierarchy_depth=RawSQL(
  33. 'SELECT COUNT(DISTINCT U0."prefix") AS "c" '
  34. 'FROM "ipam_prefix" U0 '
  35. 'WHERE (U0."prefix" >> "ipam_prefix"."prefix" '
  36. 'AND COALESCE(U0."vrf_id", 0) = COALESCE("ipam_prefix"."vrf_id", 0))',
  37. ()
  38. ),
  39. hierarchy_children=RawSQL(
  40. 'SELECT COUNT(U1."prefix") AS "c" '
  41. 'FROM "ipam_prefix" U1 '
  42. 'WHERE (U1."prefix" << "ipam_prefix"."prefix" '
  43. 'AND COALESCE(U1."vrf_id", 0) = COALESCE("ipam_prefix"."vrf_id", 0))',
  44. ()
  45. )
  46. )
  47. class VLANGroupQuerySet(RestrictedQuerySet):
  48. def annotate_utilization(self):
  49. from .models import VLAN
  50. return self.annotate(
  51. vlan_count=count_related(VLAN, 'group'),
  52. utilization=Round(F('vlan_count') / (F('max_vid') - F('min_vid') + 1.0) * 100, 2)
  53. )
  54. class VLANQuerySet(RestrictedQuerySet):
  55. def get_for_site(self, site):
  56. """
  57. Return all VLANs in the specified site
  58. """
  59. from .models import VLANGroup
  60. q = Q()
  61. q |= Q(
  62. scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
  63. scope_id=site.pk
  64. )
  65. if site.region:
  66. q |= Q(
  67. scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
  68. scope_id__in=site.region.get_ancestors(include_self=True)
  69. )
  70. if site.group:
  71. q |= Q(
  72. scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
  73. scope_id__in=site.group.get_ancestors(include_self=True)
  74. )
  75. return self.filter(
  76. Q(group__in=VLANGroup.objects.filter(q)) |
  77. Q(site=site) |
  78. Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs
  79. Q(group__isnull=True, site__isnull=True) # Global VLANs
  80. )
  81. def get_for_device(self, device):
  82. """
  83. Return all VLANs available to the specified Device.
  84. """
  85. from .models import VLANGroup
  86. # Find all relevant VLANGroups
  87. q = Q()
  88. if device.site.region:
  89. q |= Q(
  90. scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
  91. scope_id__in=device.site.region.get_ancestors(include_self=True)
  92. )
  93. if device.site.group:
  94. q |= Q(
  95. scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
  96. scope_id__in=device.site.group.get_ancestors(include_self=True)
  97. )
  98. q |= Q(
  99. scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
  100. scope_id=device.site_id
  101. )
  102. if device.location:
  103. q |= Q(
  104. scope_type=ContentType.objects.get_by_natural_key('dcim', 'location'),
  105. scope_id__in=device.location.get_ancestors(include_self=True)
  106. )
  107. if device.rack:
  108. q |= Q(
  109. scope_type=ContentType.objects.get_by_natural_key('dcim', 'rack'),
  110. scope_id=device.rack_id
  111. )
  112. # Return all applicable VLANs
  113. return self.filter(
  114. Q(group__in=VLANGroup.objects.filter(q)) |
  115. Q(site=device.site) |
  116. Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs
  117. Q(group__isnull=True, site__isnull=True) # Global VLANs
  118. )
  119. def get_for_virtualmachine(self, vm):
  120. """
  121. Return all VLANs available to the specified VirtualMachine.
  122. """
  123. from .models import VLANGroup
  124. # Find all relevant VLANGroups
  125. q = Q()
  126. site = vm.site or vm.cluster.site
  127. if vm.cluster:
  128. # Add VLANGroups scoped to the assigned cluster (or its group)
  129. q |= Q(
  130. scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'),
  131. scope_id=vm.cluster_id
  132. )
  133. if vm.cluster.group:
  134. q |= Q(
  135. scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'),
  136. scope_id=vm.cluster.group_id
  137. )
  138. if site:
  139. # Add VLANGroups scoped to the assigned site (or its group or region)
  140. q |= Q(
  141. scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
  142. scope_id=site.pk
  143. )
  144. if site.region:
  145. q |= Q(
  146. scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
  147. scope_id__in=site.region.get_ancestors(include_self=True)
  148. )
  149. if site.group:
  150. q |= Q(
  151. scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
  152. scope_id__in=site.group.get_ancestors(include_self=True)
  153. )
  154. vlan_groups = VLANGroup.objects.filter(q)
  155. # Return all applicable VLANs
  156. q = (
  157. Q(group__in=vlan_groups) |
  158. Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs
  159. Q(group__isnull=True, site__isnull=True) # Global VLANs
  160. )
  161. if site:
  162. q |= Q(site=site)
  163. return self.filter(q)