mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-18 13:38:16 -06:00
14132 add models and forms
This commit is contained in:
parent
91480299ec
commit
38d46afdbe
@ -23,6 +23,7 @@ __all__ = (
|
||||
'CustomFieldChoiceSetFilterSet',
|
||||
'CustomFieldFilterSet',
|
||||
'CustomLinkFilterSet',
|
||||
'EventRuleFilterSet',
|
||||
'ExportTemplateFilterSet',
|
||||
'ImageAttachmentFilterSet',
|
||||
'JournalEntryFilterSet',
|
||||
@ -63,6 +64,31 @@ class WebhookFilterSet(NetBoxModelFilterSet):
|
||||
)
|
||||
|
||||
|
||||
class EventRuleFilterSet(NetBoxModelFilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label=_('Search'),
|
||||
)
|
||||
content_type_id = MultiValueNumberFilter(
|
||||
field_name='content_types__id'
|
||||
)
|
||||
content_types = ContentTypeFilter()
|
||||
|
||||
class Meta:
|
||||
model = EventRule
|
||||
fields = [
|
||||
'id', 'name', 'type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end',
|
||||
'enabled',
|
||||
]
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class CustomFieldFilterSet(BaseFilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
|
@ -14,6 +14,7 @@ __all__ = (
|
||||
'CustomFieldBulkEditForm',
|
||||
'CustomFieldChoiceSetBulkEditForm',
|
||||
'CustomLinkBulkEditForm',
|
||||
'EventRuleBulkEditForm',
|
||||
'ExportTemplateBulkEditForm',
|
||||
'JournalEntryBulkEditForm',
|
||||
'SavedFilterBulkEditForm',
|
||||
@ -229,6 +230,47 @@ class WebhookBulkEditForm(NetBoxModelBulkEditForm):
|
||||
nullable_fields = ('secret', 'conditions', 'ca_file_path')
|
||||
|
||||
|
||||
class EventRuleBulkEditForm(NetBoxModelBulkEditForm):
|
||||
model = EventRule
|
||||
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=EventRule.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
enabled = forms.NullBooleanField(
|
||||
label=_('Enabled'),
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect()
|
||||
)
|
||||
type_create = forms.NullBooleanField(
|
||||
label=_('On create'),
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect()
|
||||
)
|
||||
type_update = forms.NullBooleanField(
|
||||
label=_('On update'),
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect()
|
||||
)
|
||||
type_delete = forms.NullBooleanField(
|
||||
label=_('On delete'),
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect()
|
||||
)
|
||||
type_job_start = forms.NullBooleanField(
|
||||
label=_('On job start'),
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect()
|
||||
)
|
||||
type_job_end = forms.NullBooleanField(
|
||||
label=_('On job end'),
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect()
|
||||
)
|
||||
|
||||
nullable_fields = ('conditions',)
|
||||
|
||||
|
||||
class TagBulkEditForm(BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Tag.objects.all(),
|
||||
|
@ -18,6 +18,7 @@ __all__ = (
|
||||
'CustomFieldChoiceSetImportForm',
|
||||
'CustomFieldImportForm',
|
||||
'CustomLinkImportForm',
|
||||
'EventRuleImportForm',
|
||||
'ExportTemplateImportForm',
|
||||
'JournalEntryImportForm',
|
||||
'SavedFilterImportForm',
|
||||
@ -157,6 +158,22 @@ class WebhookImportForm(NetBoxModelImportForm):
|
||||
)
|
||||
|
||||
|
||||
class EventRuleImportForm(NetBoxModelImportForm):
|
||||
content_types = CSVMultipleContentTypeField(
|
||||
label=_('Content types'),
|
||||
queryset=ContentType.objects.all(),
|
||||
limit_choices_to=FeatureQuery('eventrules'),
|
||||
help_text=_("One or more assigned object types")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = EventRule
|
||||
fields = (
|
||||
'name', 'enabled', 'content_types', 'type_create', 'type_update', 'type_delete', 'type_job_start',
|
||||
'type_job_end', 'tags'
|
||||
)
|
||||
|
||||
|
||||
class TagImportForm(CSVModelForm):
|
||||
slug = SlugField()
|
||||
|
||||
|
@ -25,6 +25,7 @@ __all__ = (
|
||||
'CustomFieldChoiceSetFilterForm',
|
||||
'CustomFieldFilterForm',
|
||||
'CustomLinkFilterForm',
|
||||
'EventRuleFilterForm',
|
||||
'ExportTemplateFilterForm',
|
||||
'ImageAttachmentFilterForm',
|
||||
'JournalEntryFilterForm',
|
||||
@ -282,6 +283,64 @@ class WebhookFilterForm(NetBoxModelFilterSetForm):
|
||||
)
|
||||
|
||||
|
||||
class EventRuleFilterForm(NetBoxModelFilterSetForm):
|
||||
model = EventRule
|
||||
tag = TagFilterField(model)
|
||||
|
||||
fieldsets = (
|
||||
(None, ('q', 'filter_id', 'tag')),
|
||||
(_('Attributes'), ('content_type_id', 'enabled')),
|
||||
(_('Events'), ('type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end')),
|
||||
)
|
||||
content_type_id = ContentTypeMultipleChoiceField(
|
||||
queryset=ContentType.objects.filter(FeatureQuery('webhooks').get_query()),
|
||||
required=False,
|
||||
label=_('Object type')
|
||||
)
|
||||
enabled = forms.NullBooleanField(
|
||||
label=_('Enabled'),
|
||||
required=False,
|
||||
widget=forms.Select(
|
||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||
)
|
||||
)
|
||||
type_create = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=forms.Select(
|
||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||
),
|
||||
label=_('Object creations')
|
||||
)
|
||||
type_update = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=forms.Select(
|
||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||
),
|
||||
label=_('Object updates')
|
||||
)
|
||||
type_delete = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=forms.Select(
|
||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||
),
|
||||
label=_('Object deletions')
|
||||
)
|
||||
type_job_start = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=forms.Select(
|
||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||
),
|
||||
label=_('Job starts')
|
||||
)
|
||||
type_job_end = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=forms.Select(
|
||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||
),
|
||||
label=_('Job terminations')
|
||||
)
|
||||
|
||||
|
||||
class TagFilterForm(SavedFiltersMixin, FilterForm):
|
||||
model = Tag
|
||||
content_type_id = ContentTypeMultipleChoiceField(
|
||||
|
@ -32,6 +32,7 @@ __all__ = (
|
||||
'CustomFieldChoiceSetForm',
|
||||
'CustomFieldForm',
|
||||
'CustomLinkForm',
|
||||
'EventRuleForm',
|
||||
'ExportTemplateForm',
|
||||
'ImageAttachmentForm',
|
||||
'JournalEntryForm',
|
||||
@ -256,6 +257,34 @@ class WebhookForm(NetBoxModelForm):
|
||||
}
|
||||
|
||||
|
||||
class EventRuleForm(NetBoxModelForm):
|
||||
content_types = ContentTypeMultipleChoiceField(
|
||||
label=_('Content types'),
|
||||
queryset=ContentType.objects.all(),
|
||||
limit_choices_to=FeatureQuery('eventrules')
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
(_('EventRule'), ('name', 'content_types', 'enabled', 'tags')),
|
||||
(_('Events'), ('type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end')),
|
||||
(_('Conditions'), ('conditions',)),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Webhook
|
||||
fields = '__all__'
|
||||
labels = {
|
||||
'type_create': _('Creations'),
|
||||
'type_update': _('Updates'),
|
||||
'type_delete': _('Deletions'),
|
||||
'type_job_start': _('Job executions'),
|
||||
'type_job_end': _('Job terminations'),
|
||||
}
|
||||
widgets = {
|
||||
'conditions': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||
}
|
||||
|
||||
|
||||
class TagForm(BootstrapMixin, forms.ModelForm):
|
||||
slug = SlugField()
|
||||
object_types = ContentTypeMultipleChoiceField(
|
||||
|
50
netbox/extras/migrations/0099_eventrule.py
Normal file
50
netbox/extras/migrations/0099_eventrule.py
Normal file
@ -0,0 +1,50 @@
|
||||
# Generated by Django 4.2.5 on 2023-10-30 21:57
|
||||
|
||||
from django.db import migrations, models
|
||||
import extras.utils
|
||||
import taggit.managers
|
||||
import utilities.json
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('extras', '0098_webhook_custom_field_data_webhook_tags'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EventRule',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('created', models.DateTimeField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
(
|
||||
'custom_field_data',
|
||||
models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
|
||||
),
|
||||
('name', models.CharField(max_length=150, unique=True)),
|
||||
('type_create', models.BooleanField(default=False)),
|
||||
('type_update', models.BooleanField(default=False)),
|
||||
('type_delete', models.BooleanField(default=False)),
|
||||
('type_job_start', models.BooleanField(default=False)),
|
||||
('type_job_end', models.BooleanField(default=False)),
|
||||
('enabled', models.BooleanField(default=True)),
|
||||
('conditions', models.JSONField(blank=True, null=True)),
|
||||
(
|
||||
'content_types',
|
||||
models.ManyToManyField(
|
||||
limit_choices_to=extras.utils.FeatureQuery('eventrules'),
|
||||
related_name='eventrules',
|
||||
to='contenttypes.contenttype',
|
||||
),
|
||||
),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'eventrule',
|
||||
'verbose_name_plural': 'eventrules',
|
||||
'ordering': ('name',),
|
||||
},
|
||||
),
|
||||
]
|
@ -30,6 +30,7 @@ __all__ = (
|
||||
'Bookmark',
|
||||
'ConfigRevision',
|
||||
'CustomLink',
|
||||
'EventRule',
|
||||
'ExportTemplate',
|
||||
'ImageAttachment',
|
||||
'JournalEntry',
|
||||
@ -38,6 +39,93 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
|
||||
"""
|
||||
A Webhook defines a request that will be sent to a remote application when an object is created, updated, and/or
|
||||
delete in NetBox. The request will contain a representation of the object, which the remote application can act on.
|
||||
Each Webhook can be limited to firing only on certain actions or certain object types.
|
||||
"""
|
||||
content_types = models.ManyToManyField(
|
||||
to=ContentType,
|
||||
related_name='eventrules',
|
||||
verbose_name=_('object types'),
|
||||
limit_choices_to=FeatureQuery('eventrules'),
|
||||
help_text=_("The object(s) to which this Event applies.")
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=150,
|
||||
unique=True
|
||||
)
|
||||
type_create = models.BooleanField(
|
||||
verbose_name=_('on create'),
|
||||
default=False,
|
||||
help_text=_("Triggers when a matching object is created.")
|
||||
)
|
||||
type_update = models.BooleanField(
|
||||
verbose_name=_('on update'),
|
||||
default=False,
|
||||
help_text=_("Triggers when a matching object is updated.")
|
||||
)
|
||||
type_delete = models.BooleanField(
|
||||
verbose_name=_('on delete'),
|
||||
default=False,
|
||||
help_text=_("Triggers when a matching object is deleted.")
|
||||
)
|
||||
type_job_start = models.BooleanField(
|
||||
verbose_name=_('on job start'),
|
||||
default=False,
|
||||
help_text=_("Triggers when a job for a matching object is started.")
|
||||
)
|
||||
type_job_end = models.BooleanField(
|
||||
verbose_name=_('on job end'),
|
||||
default=False,
|
||||
help_text=_("Triggers when a job for a matching object terminates.")
|
||||
)
|
||||
enabled = models.BooleanField(
|
||||
verbose_name=_('enabled'),
|
||||
default=True
|
||||
)
|
||||
conditions = models.JSONField(
|
||||
verbose_name=_('conditions'),
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("A set of conditions which determine whether the event will be generated.")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('name',)
|
||||
verbose_name = _('eventrule')
|
||||
verbose_name_plural = _('eventrules')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('extras:eventrule', args=[self.pk])
|
||||
|
||||
@property
|
||||
def docs_url(self):
|
||||
return f'{settings.STATIC_URL}docs/models/extras/eventrule/'
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# At least one action type must be selected
|
||||
if not any([
|
||||
self.type_create, self.type_update, self.type_delete, self.type_job_start, self.type_job_end
|
||||
]):
|
||||
raise ValidationError(
|
||||
_("At least one event type must be selected: create, update, delete, job_start, and/or job_end.")
|
||||
)
|
||||
|
||||
if self.conditions:
|
||||
try:
|
||||
ConditionSet(self.conditions)
|
||||
except ValueError as e:
|
||||
raise ValidationError({'conditions': e})
|
||||
|
||||
|
||||
class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
|
||||
"""
|
||||
A Webhook defines a request that will be sent to a remote application when an object is created, updated, and/or
|
||||
|
@ -16,6 +16,7 @@ __all__ = (
|
||||
'CustomFieldChoiceSetTable',
|
||||
'CustomFieldTable',
|
||||
'CustomLinkTable',
|
||||
'EventRuleTable',
|
||||
'ExportTemplateTable',
|
||||
'ImageAttachmentTable',
|
||||
'JournalEntryTable',
|
||||
@ -314,6 +315,48 @@ class WebhookTable(NetBoxTable):
|
||||
)
|
||||
|
||||
|
||||
class EventRuleTable(NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True
|
||||
)
|
||||
content_types = columns.ContentTypesColumn(
|
||||
verbose_name=_('Content Types'),
|
||||
)
|
||||
enabled = columns.BooleanColumn(
|
||||
verbose_name=_('Enabled'),
|
||||
)
|
||||
type_create = columns.BooleanColumn(
|
||||
verbose_name=_('Create')
|
||||
)
|
||||
type_update = columns.BooleanColumn(
|
||||
verbose_name=_('Update')
|
||||
)
|
||||
type_delete = columns.BooleanColumn(
|
||||
verbose_name=_('Delete')
|
||||
)
|
||||
type_job_start = columns.BooleanColumn(
|
||||
verbose_name=_('Job Start')
|
||||
)
|
||||
type_job_end = columns.BooleanColumn(
|
||||
verbose_name=_('Job End')
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='extras:webhook_list'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = EventRule
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete',
|
||||
'type_job_start', 'type_job_end', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'name', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', 'type_job_start',
|
||||
'type_job_end',
|
||||
)
|
||||
|
||||
|
||||
class TagTable(NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
|
@ -61,6 +61,14 @@ urlpatterns = [
|
||||
path('webhooks/delete/', views.WebhookBulkDeleteView.as_view(), name='webhook_bulk_delete'),
|
||||
path('webhooks/<int:pk>/', include(get_model_urls('extras', 'webhook'))),
|
||||
|
||||
# Event rules
|
||||
path('event-rules/', views.EventRuleListView.as_view(), name='eventrule_list'),
|
||||
path('event-rules/add/', views.EventRuleEditView.as_view(), name='eventrule_add'),
|
||||
path('event-rules/import/', views.EventRuleBulkImportView.as_view(), name='eventrule_import'),
|
||||
path('event-rules/edit/', views.EventRuleBulkEditView.as_view(), name='eventrule_bulk_edit'),
|
||||
path('event-rules/delete/', views.EventRuleBulkDeleteView.as_view(), name='eventrule_bulk_delete'),
|
||||
path('event-rules/<int:pk>/', include(get_model_urls('extras', 'eventrule'))),
|
||||
|
||||
# Tags
|
||||
path('tags/', views.TagListView.as_view(), name='tag_list'),
|
||||
path('tags/add/', views.TagEditView.as_view(), name='tag_add'),
|
||||
|
@ -396,6 +396,51 @@ class WebhookBulkDeleteView(generic.BulkDeleteView):
|
||||
table = tables.WebhookTable
|
||||
|
||||
|
||||
#
|
||||
# Event Rules
|
||||
#
|
||||
|
||||
class EventRuleListView(generic.ObjectListView):
|
||||
queryset = EventRule.objects.all()
|
||||
filterset = filtersets.EventRuleFilterSet
|
||||
filterset_form = forms.EventRuleFilterForm
|
||||
table = tables.EventRuleTable
|
||||
|
||||
|
||||
@register_model_view(EventRule)
|
||||
class EventRuleView(generic.ObjectView):
|
||||
queryset = EventRule.objects.all()
|
||||
|
||||
|
||||
@register_model_view(EventRule, 'edit')
|
||||
class EventRuleEditView(generic.ObjectEditView):
|
||||
queryset = EventRule.objects.all()
|
||||
form = forms.EventRuleForm
|
||||
|
||||
|
||||
@register_model_view(EventRule, 'delete')
|
||||
class EventRuleDeleteView(generic.ObjectDeleteView):
|
||||
queryset = EventRule.objects.all()
|
||||
|
||||
|
||||
class EventRuleBulkImportView(generic.BulkImportView):
|
||||
queryset = EventRule.objects.all()
|
||||
model_form = forms.EventRuleImportForm
|
||||
|
||||
|
||||
class EventRuleBulkEditView(generic.BulkEditView):
|
||||
queryset = EventRule.objects.all()
|
||||
filterset = filtersets.EventRuleFilterSet
|
||||
table = tables.EventRuleTable
|
||||
form = forms.EventRuleBulkEditForm
|
||||
|
||||
|
||||
class EventRuleBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = EventRule.objects.all()
|
||||
filterset = filtersets.EventRuleFilterSet
|
||||
table = tables.EventRuleTable
|
||||
|
||||
|
||||
#
|
||||
# Tags
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user