nautobot 2.0.0a3__py3-none-any.whl → 2.0.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (780) hide show
  1. nautobot/apps/api.py +6 -8
  2. nautobot/apps/forms.py +0 -2
  3. nautobot/apps/ui.py +0 -8
  4. nautobot/circuits/api/serializers.py +9 -117
  5. nautobot/circuits/api/urls.py +1 -1
  6. nautobot/circuits/api/views.py +0 -1
  7. nautobot/circuits/forms.py +0 -65
  8. nautobot/circuits/migrations/0014_related_name_changes.py +1 -1
  9. nautobot/circuits/migrations/0016_tagsfield.py +34 -0
  10. nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
  11. nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
  12. nautobot/circuits/models.py +3 -87
  13. nautobot/circuits/navigation.py +14 -69
  14. nautobot/circuits/signals.py +0 -2
  15. nautobot/circuits/tables.py +39 -1
  16. nautobot/circuits/tests/integration/test_relationships.py +9 -9
  17. nautobot/circuits/tests/test_api.py +4 -8
  18. nautobot/circuits/tests/test_filters.py +10 -4
  19. nautobot/circuits/tests/test_models.py +5 -1
  20. nautobot/circuits/tests/test_views.py +27 -5
  21. nautobot/circuits/views.py +18 -10
  22. nautobot/core/api/__init__.py +8 -2
  23. nautobot/core/api/fields.py +15 -6
  24. nautobot/core/api/filter_backends.py +3 -2
  25. nautobot/core/api/metadata.py +237 -30
  26. nautobot/core/api/mixins.py +94 -0
  27. nautobot/core/api/pagination.py +4 -0
  28. nautobot/core/api/parsers.py +154 -0
  29. nautobot/core/api/renderers.py +153 -2
  30. nautobot/core/api/schema.py +46 -2
  31. nautobot/core/api/serializers.py +377 -35
  32. nautobot/core/api/urls.py +11 -3
  33. nautobot/core/api/utils.py +174 -2
  34. nautobot/core/api/versioning.py +32 -10
  35. nautobot/core/api/views.py +266 -72
  36. nautobot/core/apps/__init__.py +138 -220
  37. nautobot/core/celery/__init__.py +112 -41
  38. nautobot/core/celery/backends.py +19 -12
  39. nautobot/core/celery/control.py +46 -0
  40. nautobot/core/celery/encoders.py +53 -0
  41. nautobot/core/celery/log.py +38 -0
  42. nautobot/core/celery/schedulers.py +23 -4
  43. nautobot/core/celery/task.py +1 -16
  44. nautobot/core/checks.py +0 -27
  45. nautobot/core/choices.py +0 -113
  46. nautobot/core/{cli.py → cli/__init__.py} +1 -1
  47. nautobot/core/cli/__main__.py +3 -0
  48. nautobot/core/constants.py +0 -24
  49. nautobot/core/context_processors.py +12 -0
  50. nautobot/core/filters.py +2 -2
  51. nautobot/core/forms/__init__.py +0 -4
  52. nautobot/core/forms/fields.py +38 -65
  53. nautobot/core/forms/forms.py +4 -1
  54. nautobot/core/forms/utils.py +0 -52
  55. nautobot/core/graphql/schema.py +4 -27
  56. nautobot/core/jobs/__init__.py +75 -0
  57. nautobot/core/management/commands/build_ui.py +255 -0
  58. nautobot/core/management/commands/generate_test_data.py +3 -2
  59. nautobot/core/management/commands/post_upgrade.py +24 -24
  60. nautobot/core/models/__init__.py +26 -1
  61. nautobot/core/models/fields.py +24 -5
  62. nautobot/core/models/generics.py +2 -42
  63. nautobot/core/models/managers.py +5 -0
  64. nautobot/core/models/name_color_content_types.py +0 -14
  65. nautobot/core/models/tree_queries.py +14 -4
  66. nautobot/core/models/utils.py +5 -6
  67. nautobot/core/models/validators.py +17 -8
  68. nautobot/core/releases.py +8 -10
  69. nautobot/core/settings.py +80 -42
  70. nautobot/core/tables.py +5 -5
  71. nautobot/core/tasks.py +4 -7
  72. nautobot/core/templates/base.html +1 -49
  73. nautobot/core/templates/base_django.html +49 -0
  74. nautobot/core/templates/base_react.html +55 -0
  75. nautobot/core/templates/buttons/export.html +6 -4
  76. nautobot/core/templates/generic/object_bulk_create.html +10 -21
  77. nautobot/core/templates/generic/object_list.html +3 -1
  78. nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
  79. nautobot/core/templates/inc/footer.html +1 -0
  80. nautobot/core/templates/inc/javascript.html +0 -14
  81. nautobot/core/templates/inc/nav_menu.html +28 -33
  82. nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
  83. nautobot/core/templates/inc/relationships_table_rows.html +2 -2
  84. nautobot/core/templates/nautobot_config.py.j2 +8 -20
  85. nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
  86. nautobot/core/templates/rest_framework/api.html +8 -0
  87. nautobot/core/templatetags/buttons.py +32 -28
  88. nautobot/core/testing/__init__.py +47 -44
  89. nautobot/core/testing/api.py +362 -47
  90. nautobot/core/testing/filters.py +1 -1
  91. nautobot/core/testing/migrations.py +2 -0
  92. nautobot/core/testing/mixins.py +22 -9
  93. nautobot/core/testing/schema.py +2 -1
  94. nautobot/core/testing/views.py +21 -46
  95. nautobot/core/tests/integration/test_filters.py +17 -8
  96. nautobot/core/tests/integration/test_navbar.py +11 -34
  97. nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
  98. nautobot/core/tests/nautobot_config.py +2 -3
  99. nautobot/core/tests/test_api.py +290 -21
  100. nautobot/core/tests/test_checks.py +0 -7
  101. nautobot/core/tests/test_filters.py +107 -59
  102. nautobot/core/tests/test_forms.py +26 -92
  103. nautobot/core/tests/test_graphql.py +110 -77
  104. nautobot/core/tests/test_logging.py +4 -0
  105. nautobot/core/tests/test_managers.py +3 -1
  106. nautobot/core/tests/test_models.py +2 -0
  107. nautobot/core/tests/test_paginator.py +3 -1
  108. nautobot/core/tests/test_releases.py +12 -12
  109. nautobot/core/tests/test_templatetags_helpers.py +4 -4
  110. nautobot/core/tests/test_utils.py +32 -68
  111. nautobot/core/tests/test_views.py +12 -15
  112. nautobot/core/utils/data.py +17 -0
  113. nautobot/core/utils/deprecation.py +9 -6
  114. nautobot/core/utils/filtering.py +8 -3
  115. nautobot/core/utils/git.py +12 -4
  116. nautobot/core/utils/lookup.py +3 -1
  117. nautobot/core/utils/requests.py +1 -104
  118. nautobot/core/views/__init__.py +1 -0
  119. nautobot/core/views/generic.py +75 -110
  120. nautobot/core/views/mixins.py +52 -61
  121. nautobot/core/views/renderers.py +6 -7
  122. nautobot/core/views/utils.py +80 -0
  123. nautobot/dcim/api/serializers.py +160 -667
  124. nautobot/dcim/api/urls.py +1 -1
  125. nautobot/dcim/api/views.py +7 -44
  126. nautobot/dcim/choices.py +2 -0
  127. nautobot/dcim/filters/__init__.py +21 -0
  128. nautobot/dcim/form_mixins.py +1 -27
  129. nautobot/dcim/forms.py +19 -765
  130. nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -1
  131. nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -13
  132. nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -1
  133. nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -1
  134. nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
  135. nautobot/dcim/migrations/0035_related_name_changes.py +1 -1
  136. nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -1
  137. nautobot/dcim/migrations/0040_tagsfield.py +109 -0
  138. nautobot/dcim/migrations/{0040_ipam__namespaces.py → 0041_ipam__namespaces.py} +1 -1
  139. nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
  140. nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
  141. nautobot/dcim/models/cables.py +3 -33
  142. nautobot/dcim/models/device_component_templates.py +6 -0
  143. nautobot/dcim/models/device_components.py +12 -198
  144. nautobot/dcim/models/devices.py +30 -143
  145. nautobot/dcim/models/locations.py +3 -64
  146. nautobot/dcim/models/power.py +3 -50
  147. nautobot/dcim/models/racks.py +7 -84
  148. nautobot/dcim/navigation.py +141 -467
  149. nautobot/dcim/signals.py +0 -2
  150. nautobot/dcim/tables/locations.py +2 -2
  151. nautobot/dcim/tables/power.py +1 -2
  152. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
  153. nautobot/dcim/templates/dcim/devicetype.html +2 -2
  154. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
  155. nautobot/dcim/templates/dcim/location.html +16 -1
  156. nautobot/dcim/templates/dcim/locationtype.html +15 -0
  157. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
  158. nautobot/dcim/templates/dcim/rackgroup.html +0 -12
  159. nautobot/dcim/tests/test_api.py +166 -81
  160. nautobot/dcim/tests/test_cablepaths.py +41 -35
  161. nautobot/dcim/tests/test_filters.py +67 -23
  162. nautobot/dcim/tests/test_forms.py +5 -205
  163. nautobot/dcim/tests/test_graphql.py +7 -2
  164. nautobot/dcim/tests/test_migrations.py +6 -11
  165. nautobot/dcim/tests/test_models.py +182 -110
  166. nautobot/dcim/tests/test_natural_ordering.py +11 -8
  167. nautobot/dcim/tests/test_signals.py +6 -3
  168. nautobot/dcim/tests/test_views.py +197 -175
  169. nautobot/dcim/urls.py +11 -16
  170. nautobot/dcim/views.py +7 -134
  171. nautobot/docs/additional-features/caching.md +6 -87
  172. nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
  173. nautobot/docs/additional-features/jobs.md +177 -195
  174. nautobot/docs/administration/nautobot-server.md +6 -21
  175. nautobot/docs/administration/replicating-nautobot.md +0 -10
  176. nautobot/docs/configuration/optional-settings.md +32 -41
  177. nautobot/docs/configuration/required-settings.md +11 -52
  178. nautobot/docs/development/application-registry.md +2 -13
  179. nautobot/docs/development/extending-models.md +15 -17
  180. nautobot/docs/development/generic-views.md +0 -2
  181. nautobot/docs/development/getting-started.md +55 -5
  182. nautobot/docs/development/navigation-menu.md +22 -93
  183. nautobot/docs/development/react-ui.md +105 -0
  184. nautobot/docs/development/role-internals.md +1 -3
  185. nautobot/docs/development/style-guide.md +6 -4
  186. nautobot/docs/index.md +3 -2
  187. nautobot/docs/installation/migrating-from-netbox.md +11 -42
  188. nautobot/docs/installation/nautobot.md +1 -1
  189. nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  190. nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  191. nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  192. nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  193. nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
  194. nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  195. nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  196. nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  197. nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  198. nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  199. nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  200. nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  201. nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  202. nautobot/docs/installation/upgrading-from-nautobot-v1.md +170 -747
  203. nautobot/docs/models/dcim/device.md +3 -0
  204. nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
  205. nautobot/docs/models/extras/computedfield.md +4 -4
  206. nautobot/docs/models/extras/gitrepository.md +3 -0
  207. nautobot/docs/models/extras/job.md +1 -0
  208. nautobot/docs/models/extras/jobbutton.md +18 -13
  209. nautobot/docs/models/extras/jobhook.md +7 -4
  210. nautobot/docs/models/extras/jobresult.md +6 -2
  211. nautobot/docs/models/extras/relationship.md +2 -2
  212. nautobot/docs/models/extras/status.md +6 -19
  213. nautobot/docs/models/ipam/ipaddress.md +3 -0
  214. nautobot/docs/models/virtualization/virtualmachine.md +3 -0
  215. nautobot/docs/plugins/development.md +83 -21
  216. nautobot/docs/release-notes/version-1.5.md +53 -0
  217. nautobot/docs/release-notes/version-2.0.md +180 -0
  218. nautobot/docs/requirements.txt +1 -0
  219. nautobot/docs/rest-api/overview.md +384 -215
  220. nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
  221. nautobot/extras/admin.py +3 -5
  222. nautobot/extras/api/customfields.py +15 -39
  223. nautobot/extras/api/fields.py +0 -11
  224. nautobot/extras/api/mixins.py +45 -0
  225. nautobot/extras/api/relationships.py +63 -158
  226. nautobot/extras/api/serializers.py +165 -700
  227. nautobot/extras/api/urls.py +1 -1
  228. nautobot/extras/api/views.py +294 -280
  229. nautobot/extras/apps.py +4 -7
  230. nautobot/extras/choices.py +11 -9
  231. nautobot/extras/constants.py +9 -3
  232. nautobot/extras/datasources/__init__.py +2 -0
  233. nautobot/extras/datasources/git.py +135 -186
  234. nautobot/extras/datasources/registry.py +25 -35
  235. nautobot/extras/filters/__init__.py +20 -19
  236. nautobot/extras/filters/mixins.py +4 -4
  237. nautobot/extras/forms/forms.py +63 -127
  238. nautobot/extras/forms/mixins.py +23 -51
  239. nautobot/extras/health_checks.py +0 -33
  240. nautobot/extras/jobs.py +387 -565
  241. nautobot/extras/management/commands/runjob.py +24 -62
  242. nautobot/extras/managers.py +30 -7
  243. nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
  244. nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -1
  245. nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -1
  246. nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -7
  247. nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -32
  248. nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -1
  249. nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -1
  250. nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
  251. nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -1
  252. nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -2
  253. nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -1
  254. nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -1
  255. nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
  256. nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -1
  257. nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
  258. nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -9
  259. nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -1
  260. nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
  261. nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
  262. nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
  263. nautobot/extras/migrations/{0077_remove_slug.py → 0078_remove_slug.py} +1 -5
  264. nautobot/extras/migrations/0079_tagsfield.py +28 -0
  265. nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
  266. nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
  267. nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
  268. nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
  269. nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
  270. nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
  271. nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
  272. nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
  273. nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
  274. nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
  275. nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
  276. nautobot/extras/models/__init__.py +2 -3
  277. nautobot/extras/models/change_logging.py +0 -36
  278. nautobot/extras/models/customfields.py +39 -33
  279. nautobot/extras/models/datasources.py +48 -50
  280. nautobot/extras/models/groups.py +5 -6
  281. nautobot/extras/models/jobs.py +189 -321
  282. nautobot/extras/models/mixins.py +0 -71
  283. nautobot/extras/models/models.py +0 -19
  284. nautobot/extras/models/relationships.py +19 -13
  285. nautobot/extras/models/roles.py +0 -34
  286. nautobot/extras/models/secrets.py +2 -26
  287. nautobot/extras/models/statuses.py +6 -5
  288. nautobot/extras/models/tags.py +2 -17
  289. nautobot/extras/navigation.py +89 -307
  290. nautobot/extras/plugins/__init__.py +3 -120
  291. nautobot/extras/plugins/utils.py +0 -3
  292. nautobot/extras/plugins/validators.py +5 -4
  293. nautobot/extras/plugins/views.py +16 -3
  294. nautobot/extras/querysets.py +1 -7
  295. nautobot/extras/registry.py +3 -0
  296. nautobot/extras/signals.py +26 -60
  297. nautobot/extras/tables.py +34 -40
  298. nautobot/extras/tasks.py +0 -12
  299. nautobot/extras/templates/extras/configcontext.html +1 -1
  300. nautobot/extras/templates/extras/configcontextschema.html +16 -1
  301. nautobot/extras/templates/extras/customfield.html +0 -13
  302. nautobot/extras/templates/extras/gitrepository.html +3 -3
  303. nautobot/extras/templates/extras/inc/jobresult.html +10 -0
  304. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  305. nautobot/extras/templates/extras/job.html +35 -25
  306. nautobot/extras/templates/extras/job_approval_request.html +15 -30
  307. nautobot/extras/templates/extras/job_detail.html +13 -31
  308. nautobot/extras/templates/extras/job_edit.html +15 -17
  309. nautobot/extras/templates/extras/jobresult.html +24 -6
  310. nautobot/extras/templates/extras/scheduledjob.html +2 -2
  311. nautobot/extras/templates/extras/secret.html +28 -0
  312. nautobot/extras/templatetags/job_buttons.py +1 -0
  313. nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
  314. nautobot/extras/test_jobs/atomic_transaction.py +53 -0
  315. nautobot/extras/test_jobs/dry_run.py +29 -0
  316. nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
  317. nautobot/extras/test_jobs/duplicate_name2.py +9 -0
  318. nautobot/extras/test_jobs/fail.py +23 -0
  319. nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
  320. nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
  321. nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
  322. nautobot/extras/test_jobs/file_upload_pass.py +25 -0
  323. nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
  324. nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
  325. nautobot/extras/test_jobs/job_button_receiver.py +28 -0
  326. nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
  327. nautobot/extras/test_jobs/job_variables.py +88 -0
  328. nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
  329. nautobot/extras/test_jobs/log_redaction.py +20 -0
  330. nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
  331. nautobot/extras/test_jobs/modify_db.py +25 -0
  332. nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
  333. nautobot/extras/test_jobs/object_var_optional.py +21 -0
  334. nautobot/extras/test_jobs/object_var_required.py +21 -0
  335. nautobot/extras/test_jobs/object_vars.py +26 -0
  336. nautobot/extras/test_jobs/pass.py +25 -0
  337. nautobot/extras/test_jobs/profiling.py +32 -0
  338. nautobot/extras/test_jobs/read_only_job.py +15 -0
  339. nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
  340. nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
  341. nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
  342. nautobot/extras/tests/integration/test_computedfields.py +1 -1
  343. nautobot/extras/tests/integration/test_configcontextschema.py +5 -3
  344. nautobot/extras/tests/integration/test_customfields.py +4 -2
  345. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  346. nautobot/extras/tests/integration/test_jobs.py +25 -27
  347. nautobot/extras/tests/integration/test_notes.py +8 -4
  348. nautobot/extras/tests/integration/test_relationships.py +2 -2
  349. nautobot/extras/tests/test_api.py +649 -642
  350. nautobot/extras/tests/test_changelog.py +3 -3
  351. nautobot/extras/tests/test_context_managers.py +5 -3
  352. nautobot/extras/tests/test_customfields.py +92 -50
  353. nautobot/extras/tests/test_datasources.py +189 -112
  354. nautobot/extras/tests/test_dynamicgroups.py +7 -8
  355. nautobot/extras/tests/test_filters.py +137 -89
  356. nautobot/extras/tests/test_forms.py +73 -75
  357. nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
  358. nautobot/extras/tests/test_jobs.py +262 -263
  359. nautobot/extras/tests/test_migrations.py +4 -3
  360. nautobot/extras/tests/test_models.py +116 -161
  361. nautobot/extras/tests/test_plugins.py +38 -60
  362. nautobot/extras/tests/test_relationships.py +167 -120
  363. nautobot/extras/tests/test_tags.py +6 -11
  364. nautobot/extras/tests/test_utils.py +31 -1
  365. nautobot/extras/tests/test_views.py +201 -145
  366. nautobot/extras/tests/test_webhooks.py +6 -2
  367. nautobot/extras/urls.py +42 -42
  368. nautobot/extras/utils.py +137 -163
  369. nautobot/extras/views.py +78 -152
  370. nautobot/ipam/api/fields.py +17 -0
  371. nautobot/ipam/api/serializers.py +58 -164
  372. nautobot/ipam/api/urls.py +1 -1
  373. nautobot/ipam/api/views.py +3 -2
  374. nautobot/ipam/apps.py +1 -2
  375. nautobot/ipam/filters.py +1 -10
  376. nautobot/ipam/forms.py +4 -177
  377. nautobot/ipam/lookups.py +1 -0
  378. nautobot/ipam/management/commands/__init__.py +0 -0
  379. nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
  380. nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -1
  381. nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -38
  382. nautobot/ipam/migrations/0020_related_name_changes.py +1 -1
  383. nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +2 -2
  384. nautobot/ipam/migrations/0028_tagsfield.py +44 -0
  385. nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
  386. nautobot/ipam/migrations/{0028_ipam__namespaces.py → 0030_ipam__namespaces.py} +77 -28
  387. nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
  388. nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
  389. nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
  390. nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
  391. nautobot/ipam/models.py +100 -236
  392. nautobot/ipam/navigation.py +36 -181
  393. nautobot/ipam/querysets.py +20 -25
  394. nautobot/ipam/signals.py +49 -6
  395. nautobot/ipam/tables.py +10 -3
  396. nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
  397. nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
  398. nautobot/ipam/templates/ipam/namespace_retrieve.html +17 -4
  399. nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
  400. nautobot/ipam/templates/ipam/prefix.html +1 -1
  401. nautobot/ipam/templates/ipam/vlangroup.html +0 -13
  402. nautobot/ipam/templates/ipam/vrf_edit.html +6 -0
  403. nautobot/ipam/tests/integration/test_prefixes.py +3 -26
  404. nautobot/ipam/tests/test_api.py +22 -19
  405. nautobot/ipam/tests/test_filters.py +59 -23
  406. nautobot/ipam/tests/test_migrations.py +6 -10
  407. nautobot/ipam/tests/test_models.py +323 -198
  408. nautobot/ipam/tests/test_ordering.py +2 -2
  409. nautobot/ipam/tests/test_querysets.py +44 -24
  410. nautobot/ipam/tests/test_views.py +73 -26
  411. nautobot/ipam/urls.py +16 -0
  412. nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
  413. nautobot/ipam/utils/migrations.py +713 -0
  414. nautobot/ipam/views.py +137 -20
  415. nautobot/project-static/docs/404.html +1178 -10
  416. nautobot/project-static/docs/additional-features/caching.html +1224 -159
  417. nautobot/project-static/docs/additional-features/change-logging.html +1180 -12
  418. nautobot/project-static/docs/additional-features/config-contexts.html +1180 -12
  419. nautobot/project-static/docs/additional-features/graphql.html +1179 -11
  420. nautobot/project-static/docs/additional-features/healthcheck.html +1180 -12
  421. nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1184 -12
  422. nautobot/project-static/docs/additional-features/jobs.html +1514 -328
  423. nautobot/project-static/docs/additional-features/napalm.html +1180 -12
  424. nautobot/project-static/docs/additional-features/prometheus-metrics.html +1180 -12
  425. nautobot/project-static/docs/additional-features/template-filters.html +1180 -12
  426. nautobot/project-static/docs/administration/celery-queues.html +1178 -10
  427. nautobot/project-static/docs/administration/nautobot-server.html +1451 -304
  428. nautobot/project-static/docs/administration/nautobot-shell.html +1178 -10
  429. nautobot/project-static/docs/administration/permissions.html +1178 -10
  430. nautobot/project-static/docs/administration/replicating-nautobot.html +1262 -113
  431. nautobot/project-static/docs/apps/index.html +1178 -10
  432. nautobot/project-static/docs/apps/nautobot-apps.html +1178 -10
  433. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1580 -426
  434. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1178 -10
  435. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3481 -1838
  436. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1178 -10
  437. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1178 -10
  438. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1185 -11
  439. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1719 -551
  440. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2062 -930
  441. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +1946 -659
  442. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1180 -12
  443. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1189 -21
  444. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9283 -6218
  445. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2734 -2122
  446. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1178 -10
  447. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2337 -1300
  448. nautobot/project-static/docs/configuration/authentication/ldap.html +1178 -10
  449. nautobot/project-static/docs/configuration/authentication/remote.html +1178 -10
  450. nautobot/project-static/docs/configuration/authentication/sso.html +1178 -10
  451. nautobot/project-static/docs/configuration/index.html +1178 -10
  452. nautobot/project-static/docs/configuration/optional-settings.html +1311 -160
  453. nautobot/project-static/docs/configuration/required-settings.html +1312 -211
  454. nautobot/project-static/docs/core-functionality/circuits.html +1178 -10
  455. nautobot/project-static/docs/core-functionality/device-types.html +1178 -10
  456. nautobot/project-static/docs/core-functionality/devices.html +1182 -10
  457. nautobot/project-static/docs/core-functionality/ipam.html +1182 -10
  458. nautobot/project-static/docs/core-functionality/power.html +1178 -10
  459. nautobot/project-static/docs/core-functionality/secrets.html +1178 -10
  460. nautobot/project-static/docs/core-functionality/services.html +1178 -10
  461. nautobot/project-static/docs/core-functionality/sites-and-racks.html +1178 -10
  462. nautobot/project-static/docs/core-functionality/tenancy.html +1178 -10
  463. nautobot/project-static/docs/core-functionality/virtualization.html +1182 -10
  464. nautobot/project-static/docs/core-functionality/vlans.html +1179 -11
  465. nautobot/project-static/docs/development/application-registry.html +1190 -42
  466. nautobot/project-static/docs/development/best-practices.html +1178 -10
  467. nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1178 -10
  468. nautobot/project-static/docs/development/extending-models.html +1238 -83
  469. nautobot/project-static/docs/development/generic-views.html +1180 -14
  470. nautobot/project-static/docs/development/getting-started.html +1365 -90
  471. nautobot/project-static/docs/development/homepage.html +1178 -10
  472. nautobot/project-static/docs/development/index.html +1178 -10
  473. nautobot/project-static/docs/development/model-features.html +1178 -10
  474. nautobot/project-static/docs/development/natural-keys.html +1178 -10
  475. nautobot/project-static/docs/development/navigation-menu.html +1215 -125
  476. nautobot/project-static/docs/development/react-ui.html +4199 -0
  477. nautobot/project-static/docs/development/release-checklist.html +1178 -10
  478. nautobot/project-static/docs/development/role-internals.html +1179 -12
  479. nautobot/project-static/docs/development/style-guide.html +1188 -19
  480. nautobot/project-static/docs/development/templates.html +1178 -10
  481. nautobot/project-static/docs/development/testing.html +1178 -10
  482. nautobot/project-static/docs/development/user-preferences.html +1178 -10
  483. nautobot/project-static/docs/docker/index.html +1178 -10
  484. nautobot/project-static/docs/index.html +1183 -12
  485. nautobot/project-static/docs/installation/centos.html +1178 -10
  486. nautobot/project-static/docs/installation/external-authentication.html +1178 -10
  487. nautobot/project-static/docs/installation/http-server.html +1178 -10
  488. nautobot/project-static/docs/installation/index.html +1178 -10
  489. nautobot/project-static/docs/installation/migrating-from-netbox.html +1305 -189
  490. nautobot/project-static/docs/installation/migrating-from-postgresql.html +1178 -10
  491. nautobot/project-static/docs/installation/nautobot.html +1179 -11
  492. nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1178 -10
  493. nautobot/project-static/docs/installation/selinux-troubleshooting.html +1178 -10
  494. nautobot/project-static/docs/installation/services.html +1178 -10
  495. nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  496. nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  497. nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  498. nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  499. nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
  500. nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  501. nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  502. nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  503. nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  504. nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  505. nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  506. nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  507. nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  508. nautobot/project-static/docs/installation/ubuntu.html +1178 -10
  509. nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +3823 -2152
  510. nautobot/project-static/docs/installation/upgrading.html +1178 -10
  511. nautobot/project-static/docs/models/circuits/circuit.html +1293 -103
  512. nautobot/project-static/docs/models/circuits/circuittermination.html +1293 -103
  513. nautobot/project-static/docs/models/circuits/circuittype.html +1293 -103
  514. nautobot/project-static/docs/models/circuits/provider.html +1293 -103
  515. nautobot/project-static/docs/models/circuits/providernetwork.html +1293 -103
  516. nautobot/project-static/docs/models/dcim/cable.html +1324 -103
  517. nautobot/project-static/docs/models/dcim/consoleport.html +1293 -103
  518. nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1293 -103
  519. nautobot/project-static/docs/models/dcim/consoleserverport.html +1293 -103
  520. nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1293 -103
  521. nautobot/project-static/docs/models/dcim/device.html +1326 -132
  522. nautobot/project-static/docs/models/dcim/devicebay.html +1293 -103
  523. nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1293 -103
  524. nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1379 -97
  525. nautobot/project-static/docs/models/dcim/devicetype.html +1293 -103
  526. nautobot/project-static/docs/models/dcim/frontport.html +1293 -103
  527. nautobot/project-static/docs/models/dcim/frontporttemplate.html +1293 -103
  528. nautobot/project-static/docs/models/dcim/interface.html +1293 -103
  529. nautobot/project-static/docs/models/dcim/interfacetemplate.html +1293 -103
  530. nautobot/project-static/docs/models/dcim/inventoryitem.html +1293 -103
  531. nautobot/project-static/docs/models/dcim/location.html +1293 -103
  532. nautobot/project-static/docs/models/dcim/locationtype.html +1293 -103
  533. nautobot/project-static/docs/models/dcim/manufacturer.html +1292 -102
  534. nautobot/project-static/docs/models/dcim/platform.html +1272 -82
  535. nautobot/project-static/docs/models/dcim/powerfeed.html +1270 -80
  536. nautobot/project-static/docs/models/dcim/poweroutlet.html +1272 -82
  537. nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1272 -82
  538. nautobot/project-static/docs/models/dcim/powerpanel.html +1270 -80
  539. nautobot/project-static/docs/models/dcim/powerport.html +1272 -82
  540. nautobot/project-static/docs/models/dcim/powerporttemplate.html +1272 -82
  541. nautobot/project-static/docs/models/dcim/rack.html +1272 -82
  542. nautobot/project-static/docs/models/dcim/rackgroup.html +1272 -82
  543. nautobot/project-static/docs/models/dcim/rackreservation.html +1272 -82
  544. nautobot/project-static/docs/models/dcim/rearport.html +1286 -96
  545. nautobot/project-static/docs/models/dcim/rearporttemplate.html +1286 -96
  546. nautobot/project-static/docs/models/dcim/region.html +1178 -10
  547. nautobot/project-static/docs/models/dcim/site.html +1178 -10
  548. nautobot/project-static/docs/models/dcim/virtualchassis.html +1284 -94
  549. nautobot/project-static/docs/models/extras/computedfield.html +1184 -16
  550. nautobot/project-static/docs/models/extras/configcontext.html +1314 -86
  551. nautobot/project-static/docs/models/extras/configcontextschema.html +1276 -86
  552. nautobot/project-static/docs/models/extras/customfield.html +1180 -12
  553. nautobot/project-static/docs/models/extras/customlink.html +1180 -12
  554. nautobot/project-static/docs/models/extras/dynamicgroup.html +1180 -12
  555. nautobot/project-static/docs/models/extras/exporttemplate.html +1180 -12
  556. nautobot/project-static/docs/models/extras/gitrepository.html +1184 -12
  557. nautobot/project-static/docs/models/extras/graphqlquery.html +1321 -86
  558. nautobot/project-static/docs/models/extras/imageattachment.html +1276 -86
  559. nautobot/project-static/docs/models/extras/job.html +1277 -86
  560. nautobot/project-static/docs/models/extras/jobbutton.html +1201 -29
  561. nautobot/project-static/docs/models/extras/jobhook.html +1188 -16
  562. nautobot/project-static/docs/models/extras/joblogentry.html +1274 -84
  563. nautobot/project-static/docs/models/extras/jobresult.html +1364 -169
  564. nautobot/project-static/docs/models/extras/note.html +1180 -12
  565. nautobot/project-static/docs/models/extras/relationship.html +1182 -14
  566. nautobot/project-static/docs/models/extras/role.html +1320 -86
  567. nautobot/project-static/docs/models/extras/secret.html +1314 -86
  568. nautobot/project-static/docs/models/extras/secretsgroup.html +1276 -86
  569. nautobot/project-static/docs/models/extras/status.html +1188 -59
  570. nautobot/project-static/docs/models/extras/tag.html +1180 -12
  571. nautobot/project-static/docs/models/extras/webhook.html +1180 -12
  572. nautobot/project-static/docs/models/ipam/ipaddress.html +1327 -102
  573. nautobot/project-static/docs/models/ipam/prefix.html +1276 -86
  574. nautobot/project-static/docs/models/ipam/rir.html +1276 -86
  575. nautobot/project-static/docs/models/ipam/routetarget.html +1276 -86
  576. nautobot/project-static/docs/models/ipam/service.html +1276 -86
  577. nautobot/project-static/docs/models/ipam/vlan.html +1276 -86
  578. nautobot/project-static/docs/models/ipam/vlangroup.html +1276 -86
  579. nautobot/project-static/docs/models/ipam/vrf.html +1276 -86
  580. nautobot/project-static/docs/models/tenancy/tenant.html +1276 -86
  581. nautobot/project-static/docs/models/tenancy/tenantgroup.html +1276 -86
  582. nautobot/project-static/docs/models/users/objectpermission.html +1314 -86
  583. nautobot/project-static/docs/models/users/token.html +1276 -86
  584. nautobot/project-static/docs/models/virtualization/cluster.html +1276 -86
  585. nautobot/project-static/docs/models/virtualization/clustergroup.html +1276 -86
  586. nautobot/project-static/docs/models/virtualization/clustertype.html +1276 -86
  587. nautobot/project-static/docs/models/virtualization/virtualmachine.html +1321 -127
  588. nautobot/project-static/docs/models/virtualization/vminterface.html +1276 -86
  589. nautobot/project-static/docs/objects.inv +0 -0
  590. nautobot/project-static/docs/plugins/development.html +1726 -495
  591. nautobot/project-static/docs/plugins/index.html +1178 -10
  592. nautobot/project-static/docs/plugins/porting-from-netbox.html +1178 -10
  593. nautobot/project-static/docs/release-notes/index.html +1178 -10
  594. nautobot/project-static/docs/release-notes/version-1.0.html +1178 -10
  595. nautobot/project-static/docs/release-notes/version-1.1.html +1178 -10
  596. nautobot/project-static/docs/release-notes/version-1.2.html +1178 -10
  597. nautobot/project-static/docs/release-notes/version-1.3.html +1178 -10
  598. nautobot/project-static/docs/release-notes/version-1.4.html +1178 -10
  599. nautobot/project-static/docs/release-notes/version-1.5.html +1608 -225
  600. nautobot/project-static/docs/release-notes/version-2.0.html +1547 -47
  601. nautobot/project-static/docs/requirements.txt +1 -0
  602. nautobot/project-static/docs/rest-api/authentication.html +1179 -11
  603. nautobot/project-static/docs/rest-api/filtering.html +1178 -10
  604. nautobot/project-static/docs/rest-api/overview.html +1841 -446
  605. nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
  606. nautobot/project-static/docs/search/search_index.json +1 -1
  607. nautobot/project-static/docs/sitemap.xml +197 -187
  608. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  609. nautobot/project-static/docs/user-guides/custom-fields.html +1178 -10
  610. nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1178 -10
  611. nautobot/project-static/docs/user-guides/getting-started/index.html +1178 -10
  612. nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1178 -10
  613. nautobot/project-static/docs/user-guides/getting-started/ipam.html +1178 -10
  614. nautobot/project-static/docs/user-guides/getting-started/platforms.html +1178 -10
  615. nautobot/project-static/docs/user-guides/getting-started/regions.html +1178 -10
  616. nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1178 -10
  617. nautobot/project-static/docs/user-guides/getting-started/tenants.html +1178 -10
  618. nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1178 -10
  619. nautobot/project-static/docs/user-guides/git-data-source.html +1178 -10
  620. nautobot/project-static/docs/user-guides/graphql.html +1178 -10
  621. nautobot/project-static/docs/user-guides/relationships.html +1178 -10
  622. nautobot/project-static/docs/user-guides/s3-django-storage.html +1178 -10
  623. nautobot/project-static/js/forms.js +16 -9
  624. nautobot/project-static/js/theme.js +5 -0
  625. nautobot/tenancy/api/serializers.py +4 -32
  626. nautobot/tenancy/api/urls.py +1 -1
  627. nautobot/tenancy/forms.py +0 -28
  628. nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
  629. nautobot/tenancy/models.py +0 -25
  630. nautobot/tenancy/navigation.py +6 -39
  631. nautobot/tenancy/templates/tenancy/tenant.html +12 -12
  632. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  633. nautobot/tenancy/tests/test_api.py +1 -3
  634. nautobot/tenancy/tests/test_filters.py +10 -5
  635. nautobot/tenancy/views.py +0 -2
  636. nautobot/ui/.eslintignore +6 -0
  637. nautobot/ui/.gitignore +10 -0
  638. nautobot/ui/.prettierignore +9 -0
  639. nautobot/ui/.prettierrc +4 -0
  640. nautobot/ui/README.md +33 -0
  641. nautobot/ui/app_imports.js.j2 +7 -0
  642. nautobot/ui/craco.config.js +46 -0
  643. nautobot/ui/jsconfig-base.json +11 -0
  644. nautobot/ui/jsconfig.json +5 -0
  645. nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
  646. nautobot/ui/package-lock.json +21451 -0
  647. nautobot/ui/package.json +70 -0
  648. nautobot/ui/public/index.html +47 -0
  649. nautobot/ui/public/logo192.png +0 -0
  650. nautobot/ui/public/logo512.png +0 -0
  651. nautobot/ui/public/manifest.json +25 -0
  652. nautobot/ui/public/nautobot_logo.svg +131 -0
  653. nautobot/ui/public/robots.txt +3 -0
  654. nautobot/ui/src/App.js +71 -0
  655. nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
  656. nautobot/ui/src/components/AppTab.js +40 -0
  657. nautobot/ui/src/components/Apps.js +60 -0
  658. nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
  659. nautobot/ui/src/components/HomePanel.js +58 -0
  660. nautobot/ui/src/components/JobHistoryTable.js +78 -0
  661. nautobot/ui/src/components/Layout.js +53 -0
  662. nautobot/ui/src/components/LoadingWidget.js +25 -0
  663. nautobot/ui/src/components/Navbar.js +116 -0
  664. nautobot/ui/src/components/NotificationPopover.js +27 -0
  665. nautobot/ui/src/components/ObjectListTable.js +209 -0
  666. nautobot/ui/src/components/ReferenceDataTag.js +35 -0
  667. nautobot/ui/src/components/RouterButton.js +10 -0
  668. nautobot/ui/src/components/RouterLink.js +10 -0
  669. nautobot/ui/src/components/SidebarNav.js +147 -0
  670. nautobot/ui/src/components/Table.js +48 -0
  671. nautobot/ui/src/components/TableItem.js +71 -0
  672. nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
  673. nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
  674. nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
  675. nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
  676. nautobot/ui/src/components/__tests__/Table.test.js +36 -0
  677. nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
  678. nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
  679. nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
  680. nautobot/ui/src/components/pagination.js +93 -0
  681. nautobot/ui/src/components/paginator.js +79 -0
  682. nautobot/ui/src/components/paginator_form.js +43 -0
  683. nautobot/ui/src/components/usePagination.js +57 -0
  684. nautobot/ui/src/constants/apiPath.js +10 -0
  685. nautobot/ui/src/constants/icons.js +15 -0
  686. nautobot/ui/src/constants/size.js +15 -0
  687. nautobot/ui/src/index.js +65 -0
  688. nautobot/ui/src/reportWebVitals.js +15 -0
  689. nautobot/ui/src/router.js +77 -0
  690. nautobot/ui/src/utils/api.js +131 -0
  691. nautobot/ui/src/utils/app-import.js +15 -0
  692. nautobot/ui/src/utils/color.js +15 -0
  693. nautobot/ui/src/utils/date.js +14 -0
  694. nautobot/ui/src/utils/index.js +15 -0
  695. nautobot/ui/src/utils/navigation.js +32 -0
  696. nautobot/ui/src/utils/session.js +64 -0
  697. nautobot/ui/src/utils/store.js +242 -0
  698. nautobot/ui/src/utils/string.js +6 -0
  699. nautobot/ui/src/utils/url.js +4 -0
  700. nautobot/ui/src/views/Home.js +138 -0
  701. nautobot/ui/src/views/InstalledApps.js +80 -0
  702. nautobot/ui/src/views/Login.js +48 -0
  703. nautobot/ui/src/views/Logout.js +20 -0
  704. nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
  705. nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
  706. nautobot/ui/src/views/__tests__/Login.test.js +15 -0
  707. nautobot/ui/src/views/generic/GenericView.js +142 -0
  708. nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
  709. nautobot/ui/src/views/generic/ObjectList.js +127 -0
  710. nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
  711. nautobot/users/admin.py +1 -1
  712. nautobot/users/api/serializers.py +51 -61
  713. nautobot/users/api/urls.py +1 -1
  714. nautobot/users/api/views.py +53 -2
  715. nautobot/users/tests/test_api.py +110 -25
  716. nautobot/virtualization/api/serializers.py +18 -130
  717. nautobot/virtualization/api/urls.py +1 -1
  718. nautobot/virtualization/api/views.py +1 -22
  719. nautobot/virtualization/forms.py +13 -99
  720. nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -1
  721. nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -11
  722. nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -1
  723. nautobot/virtualization/migrations/0018_related_name_changes.py +1 -1
  724. nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
  725. nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
  726. nautobot/virtualization/migrations/{0021_ipam__namespaces.py → 0023_ipam__namespaces.py} +2 -2
  727. nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
  728. nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
  729. nautobot/virtualization/models.py +31 -123
  730. nautobot/virtualization/navigation.py +18 -99
  731. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -1
  732. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
  733. nautobot/virtualization/tests/test_api.py +25 -26
  734. nautobot/virtualization/tests/test_filters.py +41 -15
  735. nautobot/virtualization/tests/test_models.py +31 -7
  736. nautobot/virtualization/tests/test_views.py +42 -25
  737. nautobot/virtualization/views.py +7 -6
  738. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +3 -7
  739. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +744 -602
  740. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
  741. nautobot/circuits/api/nested_serializers.py +0 -69
  742. nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
  743. nautobot/dcim/api/nested_serializers.py +0 -356
  744. nautobot/dcim/templates/dcim/device_import.html +0 -5
  745. nautobot/dcim/templates/dcim/device_import_child.html +0 -5
  746. nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
  747. nautobot/extras/api/nested_serializers.py +0 -353
  748. nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -41
  749. nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -46
  750. nautobot/extras/reports.py +0 -60
  751. nautobot/extras/scripts.py +0 -72
  752. nautobot/extras/tests/example_jobs/script_variables.py +0 -67
  753. nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
  754. nautobot/extras/tests/example_jobs/test_fail.py +0 -16
  755. nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
  756. nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
  757. nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
  758. nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
  759. nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
  760. nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
  761. nautobot/extras/tests/example_jobs/test_modify_db.py +0 -18
  762. nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
  763. nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
  764. nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
  765. nautobot/extras/tests/example_jobs/test_pass.py +0 -19
  766. nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
  767. nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
  768. nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
  769. nautobot/ipam/api/nested_serializers.py +0 -159
  770. nautobot/ipam/migrations/0029_ipam__prefix__add_parent.py +0 -31
  771. nautobot/ipam/migrations/0030_ipam__prefix__data_migration.py +0 -13
  772. nautobot/ipam/migrations/0031_ipam__ipaddress__add_parent.py +0 -41
  773. nautobot/ipam/migrations/0032_ipam__ipaddress__data_migration.py +0 -11
  774. nautobot/tenancy/api/nested_serializers.py +0 -31
  775. nautobot/users/api/nested_serializers.py +0 -67
  776. nautobot/virtualization/api/nested_serializers.py +0 -65
  777. /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
  778. /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
  779. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
  780. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
nautobot/extras/views.py CHANGED
@@ -1,4 +1,3 @@
1
- import inspect
2
1
  from datetime import timedelta
3
2
  import logging
4
3
 
@@ -26,16 +25,16 @@ from nautobot.core.models.querysets import count_related
26
25
  from nautobot.core.models.utils import pretty_print_query
27
26
  from nautobot.core.tables import ButtonsColumn
28
27
  from nautobot.core.utils.lookup import get_table_for_model
29
- from nautobot.core.utils.requests import copy_safe_request, normalize_querydict
28
+ from nautobot.core.utils.requests import normalize_querydict
30
29
  from nautobot.core.views import generic, viewsets
31
30
  from nautobot.core.views.viewsets import NautobotUIViewSet
32
31
  from nautobot.core.views.mixins import ObjectPermissionRequiredMixin
33
32
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
34
- from nautobot.core.views.utils import csv_format, prepare_cloned_fields
33
+ from nautobot.core.views.utils import prepare_cloned_fields
35
34
  from nautobot.dcim.models import Device
36
35
  from nautobot.dcim.tables import DeviceTable
37
36
  from nautobot.extras.tasks import delete_custom_field_data
38
- from nautobot.extras.utils import get_base_template, get_job_content_type, get_worker_count
37
+ from nautobot.extras.utils import get_base_template, get_worker_count
39
38
  from nautobot.ipam.tables import IPAddressTable, PrefixTable, VLANTable
40
39
  from nautobot.virtualization.models import VirtualMachine
41
40
  from nautobot.virtualization.tables import VirtualMachineTable
@@ -49,9 +48,8 @@ from .datasources import (
49
48
  get_datasource_contents,
50
49
  )
51
50
  from .filters import RoleFilterSet
52
- from .forms import RoleBulkEditForm, RoleCSVForm, RoleForm
53
- from .jobs import Job as JobClass
54
- from .jobs import get_job, run_job
51
+ from .forms import RoleBulkEditForm, RoleForm
52
+ from .jobs import get_job
55
53
  from .models import (
56
54
  ComputedField,
57
55
  ConfigContext,
@@ -65,7 +63,6 @@ from .models import (
65
63
  ImageAttachment,
66
64
  JobButton,
67
65
  JobHook,
68
- JobLogEntry,
69
66
  JobResult,
70
67
  Note,
71
68
  ObjectChange,
@@ -796,12 +793,12 @@ class GitRepositoryListView(generic.ObjectListView):
796
793
  template_name = "extras/gitrepository_list.html"
797
794
 
798
795
  def extra_context(self):
799
- git_repository_content_type = ContentType.objects.get(app_label="extras", model="gitrepository")
800
796
  # Get the newest results for each repository name
801
797
  results = {
802
- r.name: r
798
+ r.task_kwargs["repository"]: r
803
799
  for r in JobResult.objects.filter(
804
- obj_type=git_repository_content_type,
800
+ task_name__startswith="nautobot.core.jobs.GitRepository",
801
+ task_kwargs__repository__isnull=False,
805
802
  status__in=JobResultStatusChoices.READY_STATES,
806
803
  )
807
804
  .order_by("date_done")
@@ -827,14 +824,17 @@ class GitRepositoryEditView(generic.ObjectEditView):
827
824
  model_form = forms.GitRepositoryForm
828
825
  template_name = "extras/gitrepository_object_edit.html"
829
826
 
827
+ # TODO(jathan): Align with changes for v2 where we're not stashing the user on the instance for
828
+ # magical calls and instead discretely calling `repo.sync(user=user, dry_run=dry_run)`, but
829
+ # again, this will be moved to the API calls, so just something to keep in mind.
830
830
  def alter_obj(self, obj, request, url_args, url_kwargs):
831
831
  # A GitRepository needs to know the originating request when it's saved so that it can enqueue using it
832
- obj.request = request
832
+ obj.user = request.user
833
833
  return super().alter_obj(obj, request, url_args, url_kwargs)
834
834
 
835
835
  def get_return_url(self, request, obj):
836
836
  if request.method == "POST":
837
- return reverse("extras:gitrepository_result", kwargs={"slug": obj.slug})
837
+ return reverse("extras:gitrepository_result", kwargs={"pk": obj.pk})
838
838
  return super().get_return_url(request, obj)
839
839
 
840
840
 
@@ -844,17 +844,8 @@ class GitRepositoryDeleteView(generic.ObjectDeleteView):
844
844
 
845
845
  class GitRepositoryBulkImportView(generic.BulkImportView):
846
846
  queryset = GitRepository.objects.all()
847
- model_form = forms.GitRepositoryCSVForm
848
847
  table = tables.GitRepositoryBulkTable
849
848
 
850
- def _save_obj(self, obj_form, request):
851
- """Each GitRepository needs to know the originating request when it's saved so that it can enqueue using it."""
852
- instance = obj_form.save(commit=False)
853
- instance.request = request
854
- instance.save()
855
-
856
- return instance
857
-
858
849
 
859
850
  class GitRepositoryBulkEditView(generic.BulkEditView):
860
851
  queryset = GitRepository.objects.select_related("secrets_group")
@@ -883,11 +874,11 @@ class GitRepositoryBulkDeleteView(generic.BulkDeleteView):
883
874
  }
884
875
 
885
876
 
886
- def check_and_call_git_repository_function(request, slug, func):
877
+ def check_and_call_git_repository_function(request, pk, func):
887
878
  """Helper for checking Git permissions and worker availability, then calling provided function if all is well
888
879
  Args:
889
880
  request: request object.
890
- slug (str): GitRepository slug value.
881
+ pk (UUID): GitRepository pk value.
891
882
  func (function): Enqueue git repo function.
892
883
  Returns:
893
884
  HttpResponseForbidden or a redirect
@@ -899,20 +890,20 @@ def check_and_call_git_repository_function(request, slug, func):
899
890
  if not get_worker_count():
900
891
  messages.error(request, "Unable to run job: Celery worker process not running.")
901
892
  else:
902
- repository = get_object_or_404(GitRepository, slug=slug)
903
- func(repository, request)
893
+ repository = get_object_or_404(GitRepository, pk=pk)
894
+ job_result = func(repository, request.user)
904
895
 
905
- return redirect("extras:gitrepository_result", slug=slug)
896
+ return redirect(job_result.get_absolute_url())
906
897
 
907
898
 
908
899
  class GitRepositorySyncView(View):
909
- def post(self, request, slug):
910
- return check_and_call_git_repository_function(request, slug, enqueue_pull_git_repository_and_refresh_data)
900
+ def post(self, request, pk):
901
+ return check_and_call_git_repository_function(request, pk, enqueue_pull_git_repository_and_refresh_data)
911
902
 
912
903
 
913
904
  class GitRepositoryDryRunView(View):
914
- def post(self, request, slug):
915
- return check_and_call_git_repository_function(request, slug, enqueue_git_repository_diff_origin_and_local)
905
+ def post(self, request, pk):
906
+ return check_and_call_git_repository_function(request, pk, enqueue_git_repository_diff_origin_and_local)
916
907
 
917
908
 
918
909
  class GitRepositoryResultView(generic.ObjectView):
@@ -927,12 +918,7 @@ class GitRepositoryResultView(generic.ObjectView):
927
918
  return "extras.view_gitrepository"
928
919
 
929
920
  def get_extra_context(self, request, instance):
930
- git_repository_content_type = ContentType.objects.get(app_label="extras", model="gitrepository")
931
- job_result = (
932
- JobResult.objects.filter(obj_type=git_repository_content_type, name=instance.name)
933
- .order_by("-date_created")
934
- .first()
935
- )
921
+ job_result = instance.get_latest_sync()
936
922
 
937
923
  return {
938
924
  "result": job_result,
@@ -1065,7 +1051,7 @@ class JobView(ObjectPermissionRequiredMixin, View):
1065
1051
  def get_required_permission(self):
1066
1052
  return "extras.run_job"
1067
1053
 
1068
- def _get_job_model_or_404(self, class_path=None, slug=None):
1054
+ def _get_job_model_or_404(self, class_path=None, pk=None):
1069
1055
  """Helper function for get() and post()."""
1070
1056
  if class_path:
1071
1057
  try:
@@ -1073,30 +1059,32 @@ class JobView(ObjectPermissionRequiredMixin, View):
1073
1059
  except JobModel.DoesNotExist:
1074
1060
  raise Http404
1075
1061
  else:
1076
- job_model = get_object_or_404(self.queryset, slug=slug)
1062
+ job_model = get_object_or_404(self.queryset, pk=pk)
1077
1063
 
1078
1064
  return job_model
1079
1065
 
1080
- def get(self, request, class_path=None, slug=None):
1081
- job_model = self._get_job_model_or_404(class_path, slug)
1066
+ def get(self, request, class_path=None, pk=None):
1067
+ job_model = self._get_job_model_or_404(class_path, pk)
1082
1068
 
1083
1069
  try:
1084
- job_class = job_model.job_class()
1085
- initial = normalize_querydict(request.GET, form_class=job_class.as_form_class())
1070
+ try:
1071
+ job_instance = job_model.job_class()
1072
+ except TypeError as exc:
1073
+ # job_class may be None
1074
+ raise RuntimeError("Job code for this job is not currently installed or loadable") from exc
1075
+ initial = normalize_querydict(request.GET, form_class=job_instance.as_form_class())
1086
1076
  if "kwargs_from_job_result" in initial:
1087
1077
  job_result_pk = initial.pop("kwargs_from_job_result")
1088
1078
  try:
1089
- job_result = job_model.results.get(pk=job_result_pk)
1090
- # Allow explicitly specified arg values in request.GET to take precedence over the saved job_kwargs,
1091
- # for example "?kwargs_from_job_result=<UUID>&integervar=22&_commit=False"
1079
+ job_result = job_model.job_results.get(pk=job_result_pk)
1080
+ # Allow explicitly specified arg values in request.GET to take precedence over the saved task_kwargs,
1081
+ # for example "?kwargs_from_job_result=<UUID>&integervar=22"
1092
1082
  explicit_initial = initial
1093
- initial = job_result.job_kwargs.get("data", {}).copy()
1094
- commit = job_result.job_kwargs.get("commit")
1095
- if commit is not None:
1096
- initial.setdefault("_commit", commit)
1097
- task_queue = job_result.job_kwargs.get("task_queue")
1083
+ initial = job_result.task_kwargs.copy()
1084
+ task_queue = job_result.celery_kwargs.get("queue", None)
1098
1085
  if task_queue is not None:
1099
- initial.setdefault("_task_queue", task_queue)
1086
+ initial["_task_queue"] = task_queue
1087
+ initial["_profile"] = job_result.celery_kwargs.get("nautobot_job_profile", False)
1100
1088
  initial.update(explicit_initial)
1101
1089
  except JobResult.DoesNotExist:
1102
1090
  messages.warning(
@@ -1105,11 +1093,11 @@ class JobView(ObjectPermissionRequiredMixin, View):
1105
1093
  )
1106
1094
 
1107
1095
  template_name = "extras/job.html"
1108
- job_form = job_class.as_form(initial=initial)
1109
- if hasattr(job_class, "template_name"):
1096
+ job_form = job_instance.as_form(initial=initial)
1097
+ if hasattr(job_instance, "template_name"):
1110
1098
  try:
1111
- get_template(job_class.template_name)
1112
- template_name = job_class.template_name
1099
+ get_template(job_instance.template_name)
1100
+ template_name = job_instance.template_name
1113
1101
  except TemplateDoesNotExist as err:
1114
1102
  messages.error(request, f'Unable to render requested custom job template "{template_name}": {err}')
1115
1103
  except RuntimeError as err:
@@ -1128,8 +1116,8 @@ class JobView(ObjectPermissionRequiredMixin, View):
1128
1116
  },
1129
1117
  )
1130
1118
 
1131
- def post(self, request, class_path=None, slug=None):
1132
- job_model = self._get_job_model_or_404(class_path, slug)
1119
+ def post(self, request, class_path=None, pk=None):
1120
+ job_model = self._get_job_model_or_404(class_path, pk)
1133
1121
 
1134
1122
  job_form = (
1135
1123
  job_model.job_class().as_form(request.POST, request.FILES) if job_model.job_class is not None else None
@@ -1154,11 +1142,13 @@ class JobView(ObjectPermissionRequiredMixin, View):
1154
1142
  "One of these two flags must be removed before this job can be scheduled or run.",
1155
1143
  )
1156
1144
  elif job_form is not None and job_form.is_valid() and schedule_form.is_valid():
1145
+ task_queue = job_form.cleaned_data.pop("_task_queue", None)
1146
+ dryrun = job_form.cleaned_data.get("dryrun", False)
1157
1147
  # Run the job. A new JobResult is created.
1158
- commit = job_form.cleaned_data.pop("_commit")
1148
+ profile = job_form.cleaned_data.pop("_profile")
1159
1149
  schedule_type = schedule_form.cleaned_data["_schedule_type"]
1160
1150
 
1161
- if job_model.approval_required or schedule_type in JobExecutionType.SCHEDULE_CHOICES:
1151
+ if (not dryrun and job_model.approval_required) or schedule_type in JobExecutionType.SCHEDULE_CHOICES:
1162
1152
  crontab = ""
1163
1153
 
1164
1154
  if schedule_type == JobExecutionType.TYPE_IMMEDIATELY:
@@ -1184,25 +1174,16 @@ class JobView(ObjectPermissionRequiredMixin, View):
1184
1174
  else:
1185
1175
  schedule_datetime = schedule_form.cleaned_data["_schedule_start_time"]
1186
1176
 
1187
- task_kwargs = {
1188
- "data": job_model.job_class.serialize_data(job_form.cleaned_data),
1189
- "request": copy_safe_request(request),
1190
- "user": request.user.pk,
1191
- "commit": commit,
1192
- "name": job_model.class_path,
1193
- "task_queue": job_form.cleaned_data.get("_task_queue", None),
1194
- }
1195
- if task_queue:
1196
- task_kwargs["celery_kwargs"] = {"queue": task_queue}
1197
-
1177
+ celery_kwargs = {"nautobot_job_profile": profile, "queue": task_queue}
1198
1178
  scheduled_job = ScheduledJob(
1199
1179
  name=schedule_name,
1200
- task="nautobot.extras.jobs.scheduled_job_handler",
1180
+ task=job_model.job_class.registered_name,
1201
1181
  job_class=job_model.class_path,
1202
1182
  job_model=job_model,
1203
1183
  start_time=schedule_datetime,
1204
1184
  description=f"Nautobot job {schedule_name} scheduled by {request.user} for {schedule_datetime}",
1205
- kwargs=task_kwargs,
1185
+ kwargs=job_model.job_class.serialize_data(job_form.cleaned_data),
1186
+ celery_kwargs=celery_kwargs,
1206
1187
  interval=schedule_type,
1207
1188
  one_off=schedule_type == JobExecutionType.TYPE_FUTURE,
1208
1189
  queue=task_queue,
@@ -1221,17 +1202,13 @@ class JobView(ObjectPermissionRequiredMixin, View):
1221
1202
 
1222
1203
  else:
1223
1204
  # Enqueue job for immediate execution
1224
- job_content_type = get_job_content_type()
1205
+ job_kwargs = job_model.job_class.prepare_job_kwargs(job_form.cleaned_data)
1225
1206
  job_result = JobResult.enqueue_job(
1226
- run_job,
1227
- job_model.class_path,
1228
- job_content_type,
1207
+ job_model,
1229
1208
  request.user,
1230
- celery_kwargs={"queue": task_queue},
1231
- data=job_model.job_class.serialize_data(job_form.cleaned_data),
1232
- request=copy_safe_request(request),
1233
- commit=commit,
1234
- task_queue=job_form.cleaned_data.get("_task_queue", None),
1209
+ profile=profile,
1210
+ task_queue=task_queue,
1211
+ **job_model.job_class.serialize_data(job_kwargs),
1235
1212
  )
1236
1213
 
1237
1214
  return redirect("extras:jobresult", pk=job_result.pk)
@@ -1279,7 +1256,7 @@ class JobApprovalRequestView(generic.ObjectView):
1279
1256
  job's execution, rather than initial job form input.
1280
1257
  """
1281
1258
 
1282
- queryset = ScheduledJob.objects.filter(task="nautobot.extras.jobs.scheduled_job_handler").needs_approved()
1259
+ queryset = ScheduledJob.objects.needs_approved()
1283
1260
  template_name = "extras/job_approval_request.html"
1284
1261
  additional_permissions = ("extras.view_job",)
1285
1262
 
@@ -1298,8 +1275,9 @@ class JobApprovalRequestView(generic.ObjectView):
1298
1275
 
1299
1276
  if job_class is not None:
1300
1277
  # Render the form with all fields disabled
1301
- initial = instance.kwargs.get("data", {})
1302
- initial["_commit"] = instance.kwargs.get("commit", True)
1278
+ initial = instance.kwargs
1279
+ initial["_task_queue"] = instance.queue
1280
+ initial["_profile"] = instance.celery_kwargs.get("profile", False)
1303
1281
  job_form = job_class().as_form(initial=initial, approval_view=True)
1304
1282
  else:
1305
1283
  job_form = None
@@ -1335,22 +1313,17 @@ class JobApprovalRequestView(generic.ObjectView):
1335
1313
  messages.error(request, "This job cannot be run at this time")
1336
1314
  elif not JobModel.objects.check_perms(self.request.user, instance=job_model, action="run"):
1337
1315
  messages.error(request, "You do not have permission to run this job")
1316
+ elif not job_model.supports_dryrun:
1317
+ messages.error(request, "This job does not support dryrun")
1338
1318
  else:
1339
- # Immediately enqueue the job with commit=False and send the user to the normal JobResult view
1340
- job_content_type = get_job_content_type()
1341
- initial = scheduled_job.kwargs.get("data", {})
1342
- initial["_commit"] = False
1343
- celery_kwargs = scheduled_job.kwargs.get("celery_kwargs", {})
1319
+ # Immediately enqueue the job and send the user to the normal JobResult view
1320
+ job_kwargs = job_model.job_class.prepare_job_kwargs(scheduled_job.kwargs or {})
1321
+ job_kwargs["dryrun"] = True
1344
1322
  job_result = JobResult.enqueue_job(
1345
- run_job,
1346
- job_model.job_class.class_path,
1347
- job_content_type,
1323
+ job_model,
1348
1324
  request.user,
1349
- celery_kwargs=celery_kwargs,
1350
- data=job_model.job_class.serialize_data(initial),
1351
- request=copy_safe_request(request),
1352
- commit=False, # force a dry-run
1353
- task_queue=scheduled_job.kwargs.get("task_queue", None),
1325
+ celery_kwargs=scheduled_job.celery_kwargs,
1326
+ **job_model.job_class.serialize_data(job_kwargs),
1354
1327
  )
1355
1328
 
1356
1329
  return redirect("extras:jobresult", pk=job_result.pk)
@@ -1405,7 +1378,7 @@ class JobApprovalRequestView(generic.ObjectView):
1405
1378
 
1406
1379
 
1407
1380
  class ScheduledJobListView(generic.ObjectListView):
1408
- queryset = ScheduledJob.objects.filter(task="nautobot.extras.jobs.scheduled_job_handler").enabled()
1381
+ queryset = ScheduledJob.objects.enabled()
1409
1382
  table = tables.ScheduledJobTable
1410
1383
  filterset = filters.ScheduledJobFilterSet
1411
1384
  filterset_form = forms.ScheduledJobFilterForm
@@ -1419,7 +1392,7 @@ class ScheduledJobBulkDeleteView(generic.BulkDeleteView):
1419
1392
 
1420
1393
 
1421
1394
  class ScheduledJobApprovalQueueListView(generic.ObjectListView):
1422
- queryset = ScheduledJob.objects.filter(task="nautobot.extras.jobs.scheduled_job_handler").needs_approved()
1395
+ queryset = ScheduledJob.objects.needs_approved()
1423
1396
  table = tables.ScheduledJobApprovalQueueTable
1424
1397
  filterset = filters.ScheduledJobFilterSet
1425
1398
  filterset_form = forms.ScheduledJobFilterForm
@@ -1491,7 +1464,7 @@ class JobResultListView(generic.ObjectListView):
1491
1464
  List JobResults
1492
1465
  """
1493
1466
 
1494
- queryset = JobResult.objects.select_related("job_model", "obj_type", "user").prefetch_related("logs")
1467
+ queryset = JobResult.objects.select_related("job_model", "user").prefetch_related("logs")
1495
1468
  filterset = filters.JobResultFilterSet
1496
1469
  filterset_form = forms.JobResultFilterForm
1497
1470
  table = tables.JobResultTable
@@ -1512,49 +1485,14 @@ class JobResultView(generic.ObjectView):
1512
1485
  Display a JobResult and its Job data.
1513
1486
  """
1514
1487
 
1515
- queryset = JobResult.objects.prefetch_related("job_model", "obj_type", "user")
1488
+ queryset = JobResult.objects.prefetch_related("job_model", "user")
1516
1489
  template_name = "extras/jobresult.html"
1517
1490
 
1518
- def instance_to_csv(self, instance):
1519
- """Format instance to csv."""
1520
- csv_data = []
1521
- headers = JobLogEntry.csv_headers.copy()
1522
- csv_data.append(",".join(headers))
1523
-
1524
- for log_entry in instance.job_log_entries.all():
1525
- data = log_entry.to_csv()
1526
- csv_data.append(csv_format(data))
1527
-
1528
- return "\n".join(csv_data)
1529
-
1530
- def get(self, request, *args, **kwargs):
1531
- """
1532
- Generic GET handler for accessing an object by PK or slug
1533
- """
1534
- instance = get_object_or_404(self.queryset, **kwargs)
1535
-
1536
- if "export" in request.GET:
1537
- response = HttpResponse(self.instance_to_csv(instance), content_type="text/csv")
1538
- underscore_filename = f"{instance.job_model.slug.replace('-', '_')}"
1539
- formated_completion_time = instance.date_done.strftime("%Y-%m-%d_%H_%M")
1540
- filename = f"{underscore_filename}_{formated_completion_time}_logs.csv"
1541
- response["Content-Disposition"] = f"attachment; filename={filename}"
1542
- return response
1543
-
1544
- return super().get(request, *args, **kwargs)
1545
-
1546
1491
  def get_extra_context(self, request, instance):
1547
1492
  associated_record = None
1548
1493
  job_class = None
1549
1494
  if instance.job_model is not None:
1550
1495
  job_class = instance.job_model.job_class
1551
- # 2.0 TODO: remove JobResult.related_object entirely
1552
- related_object = instance.related_object
1553
- if inspect.isclass(related_object) and issubclass(related_object, JobClass):
1554
- if job_class is None:
1555
- job_class = related_object
1556
- elif related_object:
1557
- associated_record = related_object
1558
1496
 
1559
1497
  return {
1560
1498
  "job": job_class,
@@ -1587,7 +1525,6 @@ class JobButtonUIViewSet(NautobotUIViewSet):
1587
1525
  filterset_class = filters.JobButtonFilterSet
1588
1526
  filterset_form_class = forms.JobButtonFilterForm
1589
1527
  form_class = forms.JobButtonForm
1590
- lookup_field = "pk"
1591
1528
  queryset = JobButton.objects.all()
1592
1529
  serializer_class = serializers.JobButtonSerializer
1593
1530
  table_class = tables.JobButtonTable
@@ -1608,16 +1545,10 @@ class JobButtonRunView(ObjectPermissionRequiredMixin, View):
1608
1545
  job_button = JobButton.objects.get(pk=pk)
1609
1546
  job_model = job_button.job
1610
1547
  result = JobResult.enqueue_job(
1611
- func=run_job,
1612
- name=job_model.class_path,
1613
- obj_type=get_job_content_type(),
1548
+ job_model=job_model,
1614
1549
  user=request.user,
1615
- data={
1616
- "object_pk": post_data["object_pk"],
1617
- "object_model_name": post_data["object_model_name"],
1618
- },
1619
- request=copy_safe_request(request),
1620
- commit=True,
1550
+ object_pk=post_data["object_pk"],
1551
+ object_model_name=post_data["object_model_name"],
1621
1552
  )
1622
1553
  msg = f'Job enqueued. <a href="{result.get_absolute_url()}">Click here for the results.</a>'
1623
1554
  messages.info(request=request, message=mark_safe(msg))
@@ -1852,13 +1783,11 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
1852
1783
  """`Roles` UIViewSet."""
1853
1784
 
1854
1785
  queryset = Role.objects.all()
1855
- bulk_create_form_class = RoleCSVForm
1856
1786
  bulk_update_form_class = RoleBulkEditForm
1857
1787
  filterset_class = RoleFilterSet
1858
1788
  form_class = RoleForm
1859
1789
  serializer_class = serializers.RoleSerializer
1860
1790
  table_class = RoleTable
1861
- lookup_field = "pk"
1862
1791
 
1863
1792
  def get_extra_context(self, request, instance):
1864
1793
  context = super().get_extra_context(request, instance)
@@ -1999,7 +1928,6 @@ class SecretDeleteView(generic.ObjectDeleteView):
1999
1928
 
2000
1929
  class SecretBulkImportView(generic.BulkImportView):
2001
1930
  queryset = Secret.objects.all()
2002
- model_form = forms.SecretCSVForm
2003
1931
  table = tables.SecretTable
2004
1932
 
2005
1933
 
@@ -2174,7 +2102,6 @@ class StatusBulkImportView(generic.BulkImportView):
2174
2102
  """Bulk CSV import of multiple `Status` objects."""
2175
2103
 
2176
2104
  queryset = Status.objects.all()
2177
- model_form = forms.StatusCSVForm
2178
2105
  table = tables.StatusTable
2179
2106
 
2180
2107
 
@@ -2235,7 +2162,6 @@ class TagDeleteView(generic.ObjectDeleteView):
2235
2162
 
2236
2163
  class TagBulkImportView(generic.BulkImportView):
2237
2164
  queryset = Tag.objects.all()
2238
- model_form = forms.TagCSVForm
2239
2165
  table = tables.TagTable
2240
2166
 
2241
2167
 
@@ -0,0 +1,17 @@
1
+ import netaddr
2
+ from rest_framework import serializers
3
+
4
+
5
+ class IPFieldSerializer(serializers.CharField):
6
+ def to_representation(self, value):
7
+ """Convert internal (IPNetwork) representation to API (string) representation."""
8
+ return str(value)
9
+
10
+ def to_internal_value(self, value):
11
+ """Convert API (string) representation to internal (IPNetwork) representation."""
12
+ try:
13
+ return netaddr.IPNetwork(value)
14
+ except netaddr.AddrFormatError:
15
+ raise serializers.ValidationError(f"Invalid IP address format: {value}")
16
+ except (TypeError, ValueError) as e:
17
+ raise serializers.ValidationError(e)