querysets.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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_device(self, device):
  56. """
  57. Return all VLANs available to the specified Device.
  58. """
  59. from .models import VLANGroup
  60. # Find all relevant VLANGroups
  61. q = Q()
  62. if device.site.region:
  63. q |= Q(
  64. scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
  65. scope_id__in=device.site.region.get_ancestors(include_self=True)
  66. )
  67. if device.site.group:
  68. q |= Q(
  69. scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
  70. scope_id__in=device.site.group.get_ancestors(include_self=True)
  71. )
  72. q |= Q(
  73. scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
  74. scope_id=device.site_id
  75. )
  76. if device.location:
  77. q |= Q(
  78. scope_type=ContentType.objects.get_by_natural_key('dcim', 'location'),
  79. scope_id__in=device.location.get_ancestors(include_self=True)
  80. )
  81. if device.rack:
  82. q |= Q(
  83. scope_type=ContentType.objects.get_by_natural_key('dcim', 'rack'),
  84. scope_id=device.rack_id
  85. )
  86. # Return all applicable VLANs
  87. return self.filter(
  88. Q(group__in=VLANGroup.objects.filter(q)) |
  89. Q(site=device.site) |
  90. Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs
  91. Q(group__isnull=True, site__isnull=True) # Global VLANs
  92. )
  93. def get_for_virtualmachine(self, vm):
  94. """
  95. Return all VLANs available to the specified VirtualMachine.
  96. """
  97. from .models import VLANGroup
  98. # Find all relevant VLANGroups
  99. q = Q()
  100. site = vm.site or vm.cluster.site
  101. if vm.cluster:
  102. # Add VLANGroups scoped to the assigned cluster (or its group)
  103. q |= Q(
  104. scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'),
  105. scope_id=vm.cluster_id
  106. )
  107. if vm.cluster.group:
  108. q |= Q(
  109. scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'),
  110. scope_id=vm.cluster.group_id
  111. )
  112. if site:
  113. # Add VLANGroups scoped to the assigned site (or its group or region)
  114. q |= Q(
  115. scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
  116. scope_id=site.pk
  117. )
  118. if site.region:
  119. q |= Q(
  120. scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
  121. scope_id__in=site.region.get_ancestors(include_self=True)
  122. )
  123. if site.group:
  124. q |= Q(
  125. scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
  126. scope_id__in=site.group.get_ancestors(include_self=True)
  127. )
  128. vlan_groups = VLANGroup.objects.filter(q)
  129. # Return all applicable VLANs
  130. q = (
  131. Q(group__in=vlan_groups) |
  132. Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs
  133. Q(group__isnull=True, site__isnull=True) # Global VLANs
  134. )
  135. if site:
  136. q |= Q(site=site)
  137. return self.filter(q)