14132 add models and forms

This commit is contained in:
Arthur 2023-10-30 14:57:51 -07:00
parent 91480299ec
commit 38d46afdbe
10 changed files with 407 additions and 0 deletions

View File

@ -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',

View File

@ -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(),

View File

@ -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()

View File

@ -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(

View File

@ -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(

View 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',),
},
),
]

View File

@ -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

View File

@ -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'),

View File

@ -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'),

View File

@ -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
#