mirror of
https://github.com/netbox-community/netbox.git
synced 2026-02-05 06:46:25 -06:00
* Move ConfigRevision model & write migrations * Move ConfigRevision resources from extras to core * Extend migration to update original content type for ConfigRevision
This commit is contained in:
@@ -12,6 +12,7 @@ from utilities.forms.fields import ContentTypeChoiceField, DynamicModelMultipleC
|
||||
from utilities.forms.widgets import APISelectMultiple, DateTimePicker
|
||||
|
||||
__all__ = (
|
||||
'ConfigRevisionFilterForm',
|
||||
'DataFileFilterForm',
|
||||
'DataSourceFilterForm',
|
||||
'JobFilterForm',
|
||||
@@ -123,3 +124,9 @@ class JobFilterForm(SavedFiltersMixin, FilterForm):
|
||||
api_url='/api/users/users/',
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ConfigRevisionFilterForm(SavedFiltersMixin, FilterForm):
|
||||
fieldsets = (
|
||||
(None, ('q', 'filter_id')),
|
||||
)
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import copy
|
||||
import json
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from core.forms.mixins import SyncedDataMixin
|
||||
from core.models import *
|
||||
from netbox.config import get_config, PARAMS
|
||||
from netbox.forms import NetBoxModelForm
|
||||
from netbox.registry import registry
|
||||
from netbox.utils import get_data_backend_choices
|
||||
from utilities.forms import get_field_value
|
||||
from utilities.forms import BootstrapMixin, get_field_value
|
||||
from utilities.forms.fields import CommentField
|
||||
from utilities.forms.widgets import HTMXSelect
|
||||
|
||||
__all__ = (
|
||||
'ConfigRevisionForm',
|
||||
'DataSourceForm',
|
||||
'ManagedFileForm',
|
||||
)
|
||||
|
||||
EMPTY_VALUES = ('', None, [], ())
|
||||
|
||||
|
||||
class DataSourceForm(NetBoxModelForm):
|
||||
type = forms.ChoiceField(
|
||||
@@ -111,3 +117,113 @@ class ManagedFileForm(SyncedDataMixin, NetBoxModelForm):
|
||||
new_file.write(self.cleaned_data['upload_file'].read())
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class ConfigFormMetaclass(forms.models.ModelFormMetaclass):
|
||||
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
|
||||
# Emulate a declared field for each supported configuration parameter
|
||||
param_fields = {}
|
||||
for param in PARAMS:
|
||||
field_kwargs = {
|
||||
'required': False,
|
||||
'label': param.label,
|
||||
'help_text': param.description,
|
||||
}
|
||||
field_kwargs.update(**param.field_kwargs)
|
||||
param_fields[param.name] = param.field(**field_kwargs)
|
||||
attrs.update(param_fields)
|
||||
|
||||
return super().__new__(mcs, name, bases, attrs)
|
||||
|
||||
|
||||
class ConfigRevisionForm(BootstrapMixin, forms.ModelForm, metaclass=ConfigFormMetaclass):
|
||||
"""
|
||||
Form for creating a new ConfigRevision.
|
||||
"""
|
||||
|
||||
fieldsets = (
|
||||
(_('Rack Elevations'), ('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH')),
|
||||
(_('Power'), ('POWERFEED_DEFAULT_VOLTAGE', 'POWERFEED_DEFAULT_AMPERAGE', 'POWERFEED_DEFAULT_MAX_UTILIZATION')),
|
||||
(_('IPAM'), ('ENFORCE_GLOBAL_UNIQUE', 'PREFER_IPV4')),
|
||||
(_('Security'), ('ALLOWED_URL_SCHEMES',)),
|
||||
(_('Banners'), ('BANNER_LOGIN', 'BANNER_MAINTENANCE', 'BANNER_TOP', 'BANNER_BOTTOM')),
|
||||
(_('Pagination'), ('PAGINATE_COUNT', 'MAX_PAGE_SIZE')),
|
||||
(_('Validation'), ('CUSTOM_VALIDATORS', 'PROTECTION_RULES')),
|
||||
(_('User Preferences'), ('DEFAULT_USER_PREFERENCES',)),
|
||||
(_('Miscellaneous'), (
|
||||
'MAINTENANCE_MODE', 'GRAPHQL_ENABLED', 'CHANGELOG_RETENTION', 'JOB_RETENTION', 'MAPS_URL',
|
||||
)),
|
||||
(_('Config Revision'), ('comment',))
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ConfigRevision
|
||||
fields = '__all__'
|
||||
widgets = {
|
||||
'BANNER_LOGIN': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||
'BANNER_MAINTENANCE': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||
'BANNER_TOP': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||
'BANNER_BOTTOM': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||
'CUSTOM_VALIDATORS': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||
'PROTECTION_RULES': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||
'comment': forms.Textarea(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Append current parameter values to form field help texts and check for static configurations
|
||||
config = get_config()
|
||||
for param in PARAMS:
|
||||
value = getattr(config, param.name)
|
||||
|
||||
# Set the field's initial value, if it can be serialized. (This may not be the case e.g. for
|
||||
# CUSTOM_VALIDATORS, which may reference Python objects.)
|
||||
try:
|
||||
json.dumps(value)
|
||||
if type(value) in (tuple, list):
|
||||
self.fields[param.name].initial = ', '.join(value)
|
||||
else:
|
||||
self.fields[param.name].initial = value
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
# Check whether this parameter is statically configured (e.g. in configuration.py)
|
||||
if hasattr(settings, param.name):
|
||||
self.fields[param.name].disabled = True
|
||||
self.fields[param.name].help_text = _(
|
||||
'This parameter has been defined statically and cannot be modified.'
|
||||
)
|
||||
continue
|
||||
|
||||
# Set the field's help text
|
||||
help_text = self.fields[param.name].help_text
|
||||
if help_text:
|
||||
help_text += '<br />' # Line break
|
||||
help_text += _('Current value: <strong>{value}</strong>').format(value=value or '—')
|
||||
if value == param.default:
|
||||
help_text += _(' (default)')
|
||||
self.fields[param.name].help_text = help_text
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit=False)
|
||||
|
||||
# Populate JSON data on the instance
|
||||
instance.data = self.render_json()
|
||||
|
||||
if commit:
|
||||
instance.save()
|
||||
|
||||
return instance
|
||||
|
||||
def render_json(self):
|
||||
json = {}
|
||||
|
||||
# Iterate through each field and populate non-empty values
|
||||
for field_name in self.declared_fields:
|
||||
if field_name in self.cleaned_data and self.cleaned_data[field_name] not in EMPTY_VALUES:
|
||||
json[field_name] = self.cleaned_data[field_name]
|
||||
|
||||
return json
|
||||
|
||||
Reference in New Issue
Block a user