querysets.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. from __future__ import unicode_literals
  2. from django.db.models import QuerySet
  3. from django.db.models.expressions import RawSQL
  4. from .constants import IFACE_ORDERING_NAME, IFACE_ORDERING_POSITION, NONCONNECTABLE_IFACE_TYPES
  5. class InterfaceQuerySet(QuerySet):
  6. def order_naturally(self, method=IFACE_ORDERING_POSITION):
  7. """
  8. Naturally order interfaces by their type and numeric position. The sort method must be one of the defined
  9. IFACE_ORDERING_CHOICES (typically indicated by a parent Device's DeviceType).
  10. To order interfaces naturally, the `name` field is split into six distinct components: leading text (type),
  11. slot, subslot, position, channel, and virtual circuit:
  12. {type}{slot}/{subslot}/{position}/{subposition}:{channel}.{vc}
  13. Components absent from the interface name are ignored. For example, an interface named GigabitEthernet1/2/3
  14. would be parsed as follows:
  15. name = 'GigabitEthernet'
  16. slot = 1
  17. subslot = 2
  18. position = 3
  19. subposition = 0
  20. channel = None
  21. vc = 0
  22. The original `name` field is taken as a whole to serve as a fallback in the event interfaces do not match any of
  23. the prescribed fields.
  24. """
  25. sql_col = '{}.name'.format(self.model._meta.db_table)
  26. ordering = {
  27. IFACE_ORDERING_POSITION: (
  28. '_slot', '_subslot', '_position', '_subposition', '_channel', '_type', '_vc', '_id', 'name',
  29. ),
  30. IFACE_ORDERING_NAME: (
  31. '_type', '_slot', '_subslot', '_position', '_subposition', '_channel', '_vc', '_id', 'name',
  32. ),
  33. }[method]
  34. TYPE_RE = r"SUBSTRING({} FROM '^([^0-9]+)')"
  35. ID_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(\d{{1,9}})$') AS integer)"
  36. SLOT_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(\d{{1,9}})\/') AS integer)"
  37. SUBSLOT_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}\/)(\d{{1,9}})') AS integer), 0)"
  38. POSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}\/){{2}}(\d{{1,9}})') AS integer), 0)"
  39. SUBPOSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}\/){{3}}(\d{{1,9}})') AS integer), 0)"
  40. CHANNEL_RE = r"COALESCE(CAST(SUBSTRING({} FROM ':(\d{{1,9}})(\.\d{{1,9}})?$') AS integer), 0)"
  41. VC_RE = r"COALESCE(CAST(SUBSTRING({} FROM '\.(\d{{1,9}})$') AS integer), 0)"
  42. fields = {
  43. '_type': RawSQL(TYPE_RE.format(sql_col), []),
  44. '_id': RawSQL(ID_RE.format(sql_col), []),
  45. '_slot': RawSQL(SLOT_RE.format(sql_col), []),
  46. '_subslot': RawSQL(SUBSLOT_RE.format(sql_col), []),
  47. '_position': RawSQL(POSITION_RE.format(sql_col), []),
  48. '_subposition': RawSQL(SUBPOSITION_RE.format(sql_col), []),
  49. '_channel': RawSQL(CHANNEL_RE.format(sql_col), []),
  50. '_vc': RawSQL(VC_RE.format(sql_col), []),
  51. }
  52. return self.annotate(**fields).order_by(*ordering)
  53. def connectable(self):
  54. """
  55. Return only physical interfaces which are capable of being connected to other interfaces (i.e. not virtual or
  56. wireless).
  57. """
  58. return self.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES)