Source code for onbasca.base.models.relay
# SPDX-FileCopyrightText: 2022 The Tor Project, Inc.
#
# SPDX-License-Identifier: BSD-3-Clause
import logging
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from . import BaseModel
logger = logging.getLogger(__name__)
[docs]
class RelayManagerBase(models.Manager):
[docs]
def from_router_status(self, router_status, consensus=None):
# relay, _ = self.update_or_create(
# fingerprint=router_status.fingerprint, defaults=kwargs
# )
relay, _ = self.get_or_create(fingerprint=router_status.fingerprint)
relay.set_routerstatuses_bandwidth_mean()
relay.set_relaydescs_min_bandwidth_mean()
relay.save()
if consensus:
relay.consensuses.add(consensus)
return relay
[docs]
class RelayBase(BaseModel):
objects = RelayManagerBase()
fingerprint = models.CharField(primary_key=True, max_length=40)
consensuses = models.ManyToManyField("Consensus")
# useful for scaling
_routerstatuses_bandwidth_mean = models.PositiveIntegerField(
null=True, blank=True
)
_relaydescs_min_bandwidth_mean = models.PositiveIntegerField(
null=True, blank=True
)
def __str__(self):
try:
self.routerstatus_set.latest()
except ObjectDoesNotExist:
return self.fingerprint
else:
return "{}, {}".format(
self.fingerprint, self.routerstatus_set.latest().nickname
)
# for admin web
[docs]
def relaybws(self):
return self.relaybw_set.all()
[docs]
def relaybws_count(self):
return self.relaybw_set.count()
[docs]
def routerstatuses(self):
return self.routerstatus_set.all()
[docs]
def routerstatuses_count(self):
return self.routerstatuses().count()
[docs]
def routerstatus_latest(self):
try:
return self.routerstatus_set.latest()
except ObjectDoesNotExist:
# logger.warning("Relay %s without RouterStatus", self)
return None
[docs]
def consensuses_count(self):
return self.consensuses.count()
[docs]
def relaydescs(self):
return self.relaydesc_set.all()
[docs]
def relaydescs_count(self):
return self.relaydesc_set.count()
[docs]
def relaydesc_latest(self):
try:
return self.relaydesc_set.latest()
except ObjectDoesNotExist:
# logger.warning("Relay %s without RelayDesc", self)
return None
# for scanner
[docs]
def is_exit(self):
rs_last = self.routerstatus_latest()
if rs_last:
return rs_last.is_exit
return None
[docs]
def set_routerstatuses_bandwidth_mean(self):
bw = self.routerstatus_set.aggregate(
models.Avg("consensus_bandwidth")
)["consensus_bandwidth__avg"]
self._routerstatuses_bandwidth_mean = bw
self.save()
return bw
[docs]
def relaydescs_min_bandwidth_latest(self):
# Because the descriptors created from extrainfo do not have bandwidth
# values, filter by _min_bandwidth.
if (
self.relaydesc_set.filter(_min_bandwidth__isnull=False)
.filter(_min_bandwidth__gt=0)
.latest()
):
return self.relaydesc_latest().set_min_bandwidth()
return None
[docs]
def set_relaydescs_min_bandwidth_mean(self):
# _min_bandwidth is set when saving the descriptor.
self._relaydescs_min_bandwidth_mean = (
self.relaydesc_set.filter(_min_bandwidth__isnull=False)
.filter(_min_bandwidth__gt=0)
.aggregate(models.Avg("_min_bandwidth"))["_min_bandwidth__avg"]
)
self.save()
self.are_relaydescs_observed_bandwidth_0()
return self._relaydescs_min_bandwidth_mean
[docs]
def min_descriptor_consensus_bandwidth(self):
if (
self.routerstatus_latest()
and self.relaydescs_min_bandwidth_latest() is not None
):
return min(
self.routerstatus_latest().bandwidth,
self.relaydescs_min_bandwidth_latest(),
)
return None
[docs]
def are_relaydescs_observed_bandwidth_0(self):
"""Return True if any of the descriptors have observed bandwidth.
#41: This method is only to log when this happen.
It will be logged when the generator scales the bandwidth.
"""
if not self.relaydesc_set.exclude(observed_bandwidth=0):
descriptors_count = self.relaydesc_set.count()
logger.info(
"%s descriptors' observed bandwidth are 0 for relay %s (%s), "
"published on %s.",
descriptors_count,
self.fingerprint,
self.relaydesc_set.latest().nickname,
self.relaydesc_set.latest().published,
)
return True
return False