|
@@ -13,7 +13,6 @@ from django.core.exceptions import ValidationError
|
|
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
|
from django.db import models
|
|
|
from django.db.models import Count, Q, ObjectDoesNotExist
|
|
|
-from django.db.models.expressions import RawSQL
|
|
|
from django.urls import reverse
|
|
|
from django.utils.encoding import python_2_unicode_compatible
|
|
|
|
|
@@ -27,6 +26,7 @@ from utilities.models import CreatedUpdatedModel
|
|
|
from utilities.utils import csv_format
|
|
|
from .constants import *
|
|
|
from .fields import ASNField, MACAddressField
|
|
|
+from .querysets import InterfaceQuerySet
|
|
|
|
|
|
|
|
|
#
|
|
@@ -687,72 +687,6 @@ class PowerOutletTemplate(models.Model):
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
-class InterfaceQuerySet(models.QuerySet):
|
|
|
-
|
|
|
- def order_naturally(self, method=IFACE_ORDERING_POSITION):
|
|
|
- """
|
|
|
- Naturally order interfaces by their type and numeric position. The sort method must be one of the defined
|
|
|
- IFACE_ORDERING_CHOICES (typically indicated by a parent Device's DeviceType).
|
|
|
-
|
|
|
- To order interfaces naturally, the `name` field is split into six distinct components: leading text (type),
|
|
|
- slot, subslot, position, channel, and virtual circuit:
|
|
|
-
|
|
|
- {type}{slot}/{subslot}/{position}/{subposition}:{channel}.{vc}
|
|
|
-
|
|
|
- Components absent from the interface name are ignored. For example, an interface named GigabitEthernet1/2/3
|
|
|
- would be parsed as follows:
|
|
|
-
|
|
|
- name = 'GigabitEthernet'
|
|
|
- slot = 1
|
|
|
- subslot = 2
|
|
|
- position = 3
|
|
|
- subposition = 0
|
|
|
- channel = None
|
|
|
- vc = 0
|
|
|
-
|
|
|
- The original `name` field is taken as a whole to serve as a fallback in the event interfaces do not match any of
|
|
|
- the prescribed fields.
|
|
|
- """
|
|
|
- sql_col = '{}.name'.format(self.model._meta.db_table)
|
|
|
- ordering = {
|
|
|
- IFACE_ORDERING_POSITION: (
|
|
|
- '_slot', '_subslot', '_position', '_subposition', '_channel', '_vc', '_type', '_id', 'name',
|
|
|
- ),
|
|
|
- IFACE_ORDERING_NAME: (
|
|
|
- '_type', '_slot', '_subslot', '_position', '_subposition', '_channel', '_vc', '_id', 'name',
|
|
|
- ),
|
|
|
- }[method]
|
|
|
-
|
|
|
- TYPE_RE = r"SUBSTRING({} FROM '^([^0-9]+)')"
|
|
|
- ID_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)([0-9]+)$') AS integer)"
|
|
|
- SLOT_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)([0-9]+)\/') AS integer)"
|
|
|
- SUBSLOT_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+\/)([0-9]+)') AS integer)"
|
|
|
- POSITION_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+\/){{2}}([0-9]+)') AS integer)"
|
|
|
- SUBPOSITION_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+\/){{3}}([0-9]+)') AS integer)"
|
|
|
- CHANNEL_RE = r"COALESCE(CAST(SUBSTRING({} FROM ':([0-9]+)(\.[0-9]+)?$') AS integer), 0)"
|
|
|
- VC_RE = r"COALESCE(CAST(SUBSTRING({} FROM '\.([0-9]+)$') AS integer), 0)"
|
|
|
-
|
|
|
- fields = {
|
|
|
- '_type': RawSQL(TYPE_RE.format(sql_col), []),
|
|
|
- '_id': RawSQL(ID_RE.format(sql_col), []),
|
|
|
- '_slot': RawSQL(SLOT_RE.format(sql_col), []),
|
|
|
- '_subslot': RawSQL(SUBSLOT_RE.format(sql_col), []),
|
|
|
- '_position': RawSQL(POSITION_RE.format(sql_col), []),
|
|
|
- '_subposition': RawSQL(SUBPOSITION_RE.format(sql_col), []),
|
|
|
- '_channel': RawSQL(CHANNEL_RE.format(sql_col), []),
|
|
|
- '_vc': RawSQL(VC_RE.format(sql_col), []),
|
|
|
- }
|
|
|
-
|
|
|
- return self.annotate(**fields).order_by(*ordering)
|
|
|
-
|
|
|
- def connectable(self):
|
|
|
- """
|
|
|
- Return only physical interfaces which are capable of being connected to other interfaces (i.e. not virtual or
|
|
|
- wireless).
|
|
|
- """
|
|
|
- return self.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES)
|
|
|
-
|
|
|
-
|
|
|
@python_2_unicode_compatible
|
|
|
class InterfaceTemplate(models.Model):
|
|
|
"""
|