Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions netbox/core/migrations/0019_configrevision_active.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 5.2.5 on 2025-09-09 16:48

from django.db import migrations, models


def get_active(apps, schema_editor):
from django.core.cache import cache
ConfigRevision = apps.get_model('core', 'ConfigRevision')
version = None
revision = None

# Try and get the latest version from cache
try:
version = cache.get('config_version')
except Exception:
pass

# If there is a version in cache, attempt to set revision to the current version from cache
# If the version in cache does not exist or there is no version, try the lastest revision in the database
if not version or (version and not (revision := ConfigRevision.objects.filter(pk=version).first())):
revision = ConfigRevision.objects.order_by('-created').first()

# If there is a revision set, set the active revision
if revision:
revision.active = True
revision.save()


class Migration(migrations.Migration):

dependencies = [
('core', '0018_concrete_objecttype'),
]

operations = [
migrations.AddField(
model_name='configrevision',
name='active',
field=models.BooleanField(default=False),
),
migrations.RunPython(code=get_active, reverse_code=migrations.RunPython.noop),
migrations.AddConstraint(
model_name='configrevision',
constraint=models.UniqueConstraint(
condition=models.Q(('active', True)), fields=('active',), name='unique_active_config_revision'
),
),
]
17 changes: 16 additions & 1 deletion netbox/core/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class ConfigRevision(models.Model):
"""
An atomic revision of NetBox's configuration.
"""
active = models.BooleanField(
default=False
)
created = models.DateTimeField(
verbose_name=_('created'),
auto_now_add=True
Expand All @@ -35,6 +38,13 @@ class Meta:
ordering = ['-created']
verbose_name = _('config revision')
verbose_name_plural = _('config revisions')
constraints = [
models.UniqueConstraint(
fields=('active',),
condition=models.Q(active=True),
name='unique_active_config_revision',
)
]

def __str__(self):
if not self.pk:
Expand All @@ -59,8 +69,13 @@ def activate(self):
"""
cache.set('config', self.data, None)
cache.set('config_version', self.pk, None)

# Set all instances of ConfigRevision to false and set this instance to true
ConfigRevision.objects.all().update(active=False)
ConfigRevision.objects.filter(pk=self.pk).update(active=True)

activate.alters_data = True

@property
def is_active(self):
return cache.get('config_version') == self.pk
return self.active
9 changes: 7 additions & 2 deletions netbox/netbox/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,16 @@ def _populate_from_db(self):
from core.models import ConfigRevision

try:
revision = ConfigRevision.objects.last()
# Enforce the creation date as the ordering parameter
revision = ConfigRevision.objects.get(active=True)
logger.debug(f"Loaded active configuration revision #{revision.pk}")
except (ConfigRevision.DoesNotExist, ConfigRevision.MultipleObjectsReturned):
logger.warning("No active configuration revision found - falling back to most recent")
revision = ConfigRevision.objects.order_by('-created').first()
if revision is None:
logger.debug("No previous configuration found in database; proceeding with default values")
return
logger.debug("Loaded configuration data from database")
logger.debug(f"Using fallback configuration revision #{revision.pk}")
except DatabaseError:
# The database may not be available yet (e.g. when running a management command)
logger.warning("Skipping config initialization (database unavailable)")
Expand Down