from django.db import models
from .. import exc
from .attribute import Attribute
from .resource import Resource
[docs]
class Protocol(Resource):
"""
Representation of a routing protocol
"""
# Derive site from device; returns Site object (not site_id)
site_source_field = "device"
site_returns_object = True
site = models.ForeignKey(
"Site",
db_index=True,
blank=True,
null=True,
related_name="protocols",
on_delete=models.PROTECT,
verbose_name="Site",
help_text=(
"Unique ID of the Site this Protocol is under. If not set, this "
"be inherited off of the device's site"
),
)
type = models.ForeignKey(
"ProtocolType",
db_index=True,
related_name="protocols",
on_delete=models.CASCADE,
help_text="The type of this Protocol",
)
device = models.ForeignKey(
"Device",
db_index=True,
null=False,
related_name="protocols",
on_delete=models.CASCADE,
help_text="Device that this protocol is running on",
)
interface = models.ForeignKey(
"Interface",
db_index=True,
blank=True,
null=True,
related_name="protocols",
on_delete=models.CASCADE,
help_text=(
"Interface this protocol is running on. Either interface or"
" circuit must be populated."
),
)
circuit = models.ForeignKey(
"Circuit",
db_index=True,
blank=True,
null=True,
related_name="protocols",
on_delete=models.CASCADE,
help_text="Circuit that this protocol is running over.",
)
auth_string = models.CharField(
max_length=255,
default="",
blank=True,
verbose_name="Auth String",
help_text="Authentication string (such as MD5 sum)",
)
description = models.CharField(
max_length=255,
default="",
blank=True,
help_text="Description for this Protocol",
)
def __str__(self):
description = str(self.type)
if self.circuit:
description += " over %s" % self.circuit
elif self.interface:
description += " on %s" % self.interface
else:
description += " on %s" % self.device
return description
class Meta:
ordering = ("device",)
[docs]
def clean_circuit(self, value):
"""Ensure at least one endpoint on the circuit is on this device"""
if value and value.interface_for(self.device) is None:
raise exc.ValidationError(
{
"circuit": (
"At least one endpoint of the circuit must match the "
"device"
)
}
)
return value
[docs]
def clean_interface(self, value):
"""
Ensure that the interface is bound to the same device this Protocol is
bound to.
"""
if value and value.device != self.device:
raise exc.ValidationError(
{
"interface": (
"The interface must be on the same device that this"
" Protocol is on"
)
}
)
return value
[docs]
def clean_type(self, value):
"""Ensure that ProtocolType matches our site."""
if self.site != value.site:
raise exc.ValidationError(
{"type": "The type must be on the same site as this Protocol"}
)
return value
[docs]
def set_attributes(self, attributes, valid_attributes=None, partial=False):
"""
Ensure that all attributes are set that are required by the set
ProtocolType.
"""
required = self.type.get_required_attributes()
if valid_attributes is None:
valid_attributes = Attribute.all_by_name(
"Protocol", site=self.site
)
# Temporarily mark required attributes as ``required`` at run-time for
# injecting required_attributes into validation.
for r in required:
if r in valid_attributes:
valid_attributes[r].required = True
return super().set_attributes(
attributes, valid_attributes=valid_attributes, partial=partial
)
[docs]
def clean_fields(self, exclude=None):
self.site = self.clean_site(self.site)
self.type = self.clean_type(self.type)
self.interface = self.clean_interface(self.interface)
self.circuit = self.clean_circuit(self.circuit)
# TODO(jathan): type, device, interface, circuit need indexing. We might
# consider caching these values ON the Protocol object similarly how we've
# done it with other objects, so that the related lookups are only done on
# update.
def to_dict(self):
return {
"id": self.id,
"site": self.site_id,
"type": self.type.name,
"device": self.device.hostname,
"interface": self.interface and self.interface.name_slug,
"circuit": self.circuit and self.circuit.name_slug,
"description": self.description,
"auth_string": self.auth_string,
"attributes": self.get_attributes(),
}