bulk_import.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. import re
  2. from django import forms
  3. from django.contrib.postgres.forms import SimpleArrayField
  4. from django.core.exceptions import ObjectDoesNotExist
  5. from django.utils.translation import gettext_lazy as _
  6. from core.models import DataFile, DataSource, ObjectType
  7. from extras.choices import *
  8. from extras.models import *
  9. from netbox.events import get_event_type_choices
  10. from netbox.forms import NetBoxModelImportForm, OwnerCSVMixin, PrimaryModelImportForm
  11. from users.models import Group, User
  12. from utilities.forms import CSVModelForm
  13. from utilities.forms.fields import (
  14. CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, CSVMultipleChoiceField,
  15. CSVMultipleContentTypeField, SlugField,
  16. )
  17. __all__ = (
  18. 'ConfigContextProfileImportForm',
  19. 'ConfigTemplateImportForm',
  20. 'CustomFieldChoiceSetImportForm',
  21. 'CustomFieldImportForm',
  22. 'CustomLinkImportForm',
  23. 'EventRuleImportForm',
  24. 'ExportTemplateImportForm',
  25. 'JournalEntryImportForm',
  26. 'NotificationGroupImportForm',
  27. 'SavedFilterImportForm',
  28. 'TagImportForm',
  29. 'WebhookImportForm',
  30. )
  31. class CustomFieldImportForm(OwnerCSVMixin, CSVModelForm):
  32. object_types = CSVMultipleContentTypeField(
  33. label=_('Object types'),
  34. queryset=ObjectType.objects.with_feature('custom_fields'),
  35. help_text=_("One or more assigned object types")
  36. )
  37. type = CSVChoiceField(
  38. label=_('Type'),
  39. choices=CustomFieldTypeChoices,
  40. help_text=_('Field data type (e.g. text, integer, etc.)')
  41. )
  42. related_object_type = CSVContentTypeField(
  43. label=_('Object type'),
  44. queryset=ObjectType.objects.public(),
  45. required=False,
  46. help_text=_("Object type (for object or multi-object fields)")
  47. )
  48. choice_set = CSVModelChoiceField(
  49. label=_('Choice set'),
  50. queryset=CustomFieldChoiceSet.objects.all(),
  51. to_field_name='name',
  52. required=False,
  53. help_text=_('Choice set (for selection fields)')
  54. )
  55. ui_visible = CSVChoiceField(
  56. label=_('UI visible'),
  57. choices=CustomFieldUIVisibleChoices,
  58. required=False,
  59. help_text=_('Whether the custom field is displayed in the UI')
  60. )
  61. ui_editable = CSVChoiceField(
  62. label=_('UI editable'),
  63. choices=CustomFieldUIEditableChoices,
  64. required=False,
  65. help_text=_('Whether the custom field is editable in the UI')
  66. )
  67. class Meta:
  68. model = CustomField
  69. fields = (
  70. 'name', 'label', 'group_name', 'type', 'object_types', 'related_object_type', 'required', 'unique',
  71. 'description', 'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
  72. 'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable', 'owner', 'comments',
  73. )
  74. class CustomFieldChoiceSetImportForm(OwnerCSVMixin, CSVModelForm):
  75. base_choices = CSVChoiceField(
  76. choices=CustomFieldChoiceSetBaseChoices,
  77. required=False,
  78. help_text=_('The base set of predefined choices to use (if any)')
  79. )
  80. extra_choices = SimpleArrayField(
  81. base_field=forms.CharField(),
  82. required=False,
  83. help_text=_(
  84. 'Quoted string of comma-separated field choices with optional labels separated by colon: '
  85. '"choice1:First Choice,choice2:Second Choice"'
  86. )
  87. )
  88. class Meta:
  89. model = CustomFieldChoiceSet
  90. fields = (
  91. 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', 'owner',
  92. )
  93. def clean_extra_choices(self):
  94. if isinstance(self.cleaned_data['extra_choices'], list):
  95. data = []
  96. for line in self.cleaned_data['extra_choices']:
  97. try:
  98. value, label = re.split(r'(?<!\\):', line, maxsplit=1)
  99. value = value.replace('\\:', ':')
  100. label = label.replace('\\:', ':')
  101. except ValueError:
  102. value, label = line, line
  103. data.append((value, label))
  104. return data
  105. class CustomLinkImportForm(OwnerCSVMixin, CSVModelForm):
  106. object_types = CSVMultipleContentTypeField(
  107. label=_('Object types'),
  108. queryset=ObjectType.objects.with_feature('custom_links'),
  109. help_text=_("One or more assigned object types")
  110. )
  111. button_class = CSVChoiceField(
  112. label=_('button class'),
  113. required=False,
  114. choices=CustomLinkButtonClassChoices,
  115. help_text=_('The class of the first link in a group will be used for the dropdown button')
  116. )
  117. class Meta:
  118. model = CustomLink
  119. fields = (
  120. 'name', 'object_types', 'enabled', 'weight', 'group_name', 'button_class', 'new_window', 'link_text',
  121. 'link_url', 'owner',
  122. )
  123. class ExportTemplateImportForm(OwnerCSVMixin, CSVModelForm):
  124. object_types = CSVMultipleContentTypeField(
  125. label=_('Object types'),
  126. queryset=ObjectType.objects.with_feature('export_templates'),
  127. help_text=_("One or more assigned object types")
  128. )
  129. class Meta:
  130. model = ExportTemplate
  131. fields = (
  132. 'name', 'object_types', 'description', 'environment_params', 'mime_type', 'file_name', 'file_extension',
  133. 'as_attachment', 'template_code', 'owner',
  134. )
  135. class ConfigContextProfileImportForm(PrimaryModelImportForm):
  136. class Meta:
  137. model = ConfigContextProfile
  138. fields = [
  139. 'name', 'description', 'schema', 'owner', 'comments', 'tags',
  140. ]
  141. class ConfigTemplateImportForm(OwnerCSVMixin, CSVModelForm):
  142. data_source = CSVModelChoiceField(
  143. label=_('Data source'),
  144. queryset=DataSource.objects.all(),
  145. required=False,
  146. to_field_name='name',
  147. help_text=_('Data source which provides the data file')
  148. )
  149. data_file = CSVModelChoiceField(
  150. label=_('Data file'),
  151. queryset=DataFile.objects.all(),
  152. required=False,
  153. to_field_name='path',
  154. help_text=_('Data file containing the template code')
  155. )
  156. auto_sync_enabled = forms.BooleanField(
  157. required=False,
  158. label=_('Auto sync enabled'),
  159. help_text=_("Enable automatic synchronization of template content when the data file is updated")
  160. )
  161. class Meta:
  162. model = ConfigTemplate
  163. fields = (
  164. 'name', 'description', 'template_code', 'data_source', 'data_file', 'auto_sync_enabled',
  165. 'environment_params', 'mime_type', 'file_name', 'file_extension', 'as_attachment', 'owner', 'tags',
  166. )
  167. def clean(self):
  168. super().clean()
  169. # Make sure template_code is None when it's not included in the uploaded data
  170. if not self.data.get('template_code') and not self.data.get('data_file'):
  171. raise forms.ValidationError(_("Must specify either local content or a data file"))
  172. return self.cleaned_data['template_code']
  173. class SavedFilterImportForm(OwnerCSVMixin, CSVModelForm):
  174. object_types = CSVMultipleContentTypeField(
  175. label=_('Object types'),
  176. queryset=ObjectType.objects.all(),
  177. help_text=_("One or more assigned object types")
  178. )
  179. class Meta:
  180. model = SavedFilter
  181. fields = (
  182. 'name', 'slug', 'object_types', 'description', 'weight', 'enabled', 'shared', 'parameters', 'owner',
  183. )
  184. class WebhookImportForm(OwnerCSVMixin, NetBoxModelImportForm):
  185. class Meta:
  186. model = Webhook
  187. fields = (
  188. 'name', 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template',
  189. 'secret', 'ssl_verification', 'ca_file_path', 'description', 'owner', 'tags'
  190. )
  191. class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
  192. object_types = CSVMultipleContentTypeField(
  193. label=_('Object types'),
  194. queryset=ObjectType.objects.with_feature('event_rules'),
  195. help_text=_("One or more assigned object types")
  196. )
  197. event_types = CSVMultipleChoiceField(
  198. choices=get_event_type_choices(),
  199. label=_('Event types'),
  200. help_text=_('The event type(s) which will trigger this rule')
  201. )
  202. action_object = forms.CharField(
  203. label=_('Action object'),
  204. required=True,
  205. help_text=_('Webhook name or script as dotted path module.Class')
  206. )
  207. class Meta:
  208. model = EventRule
  209. fields = (
  210. 'name', 'description', 'enabled', 'conditions', 'object_types', 'event_types', 'action_type',
  211. 'owner', 'comments', 'tags'
  212. )
  213. def clean(self):
  214. super().clean()
  215. action_object = self.cleaned_data.get('action_object')
  216. action_type = self.cleaned_data.get('action_type')
  217. if action_object and action_type:
  218. # Webhook
  219. if action_type == EventRuleActionChoices.WEBHOOK:
  220. try:
  221. webhook = Webhook.objects.get(name=action_object)
  222. except Webhook.DoesNotExist:
  223. raise forms.ValidationError(_("Webhook {name} not found").format(name=action_object))
  224. self.instance.action_object = webhook
  225. # Script
  226. elif action_type == EventRuleActionChoices.SCRIPT:
  227. from extras.scripts import get_module_and_script
  228. module_name, script_name = action_object.split('.', 1)
  229. try:
  230. script = get_module_and_script(module_name, script_name)[1]
  231. except ObjectDoesNotExist:
  232. raise forms.ValidationError(_("Script {name} not found").format(name=action_object))
  233. self.instance.action_object = script
  234. self.instance.action_object_type = ObjectType.objects.get_for_model(script, for_concrete_model=False)
  235. class TagImportForm(OwnerCSVMixin, CSVModelForm):
  236. slug = SlugField()
  237. object_types = CSVMultipleContentTypeField(
  238. label=_('Object types'),
  239. queryset=ObjectType.objects.with_feature('tags'),
  240. help_text=_("One or more assigned object types"),
  241. required=False,
  242. )
  243. class Meta:
  244. model = Tag
  245. fields = (
  246. 'name', 'slug', 'color', 'weight', 'description', 'object_types', 'owner',
  247. )
  248. class JournalEntryImportForm(NetBoxModelImportForm):
  249. assigned_object_type = CSVContentTypeField(
  250. queryset=ObjectType.objects.all(),
  251. label=_('Assigned object type'),
  252. )
  253. kind = CSVChoiceField(
  254. label=_('Kind'),
  255. choices=JournalEntryKindChoices,
  256. help_text=_('The classification of entry')
  257. )
  258. comments = forms.CharField(
  259. label=_('Comments'),
  260. required=True
  261. )
  262. class Meta:
  263. model = JournalEntry
  264. fields = (
  265. 'assigned_object_type', 'assigned_object_id', 'created_by', 'kind', 'comments', 'tags'
  266. )
  267. class NotificationGroupImportForm(CSVModelForm):
  268. users = CSVModelMultipleChoiceField(
  269. label=_('Users'),
  270. queryset=User.objects.all(),
  271. required=False,
  272. to_field_name='username',
  273. help_text=_('User names separated by commas, encased with double quotes')
  274. )
  275. groups = CSVModelMultipleChoiceField(
  276. label=_('Groups'),
  277. queryset=Group.objects.all(),
  278. required=False,
  279. to_field_name='name',
  280. help_text=_('Group names separated by commas, encased with double quotes')
  281. )
  282. class Meta:
  283. model = NotificationGroup
  284. fields = ('name', 'description', 'users', 'groups')