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
@@ -12,14 +12,10 @@ from django.urls import get_resolver, URLPattern
12
12
 
13
13
  from nautobot.core.apps import (
14
14
  NautobotConfig,
15
- NavMenuButton,
16
- NavMenuGroup,
17
- NavMenuItem,
18
15
  NavMenuTab,
19
16
  register_menu_items,
20
17
  register_homepage_panels,
21
18
  )
22
- from nautobot.core.choices import ButtonColorChoices
23
19
  from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
24
20
  from nautobot.core.signals import nautobot_database_ready
25
21
  from nautobot.extras.choices import BannerClassChoices
@@ -76,11 +72,6 @@ class NautobotAppConfig(NautobotConfig):
76
72
  # along with the plugin.
77
73
  installed_apps = []
78
74
 
79
- # Cacheops configuration. Cache all operations by default.
80
- caching_config = {
81
- "*": {"ops": "all"},
82
- }
83
-
84
75
  # Default constance configuration parameters
85
76
  constance_config = {}
86
77
 
@@ -244,7 +235,6 @@ class NautobotAppConfig(NautobotConfig):
244
235
  # TODO(jathan): This is fine for now, but as we expand the functionality
245
236
  # of plugins, we'll need to consider something like pydantic or attrs.
246
237
  setting_validations = {
247
- "caching_config": dict,
248
238
  "default_settings": dict,
249
239
  "installed_apps": list,
250
240
  "middleware": list,
@@ -515,124 +505,17 @@ def register_filter_extensions(filter_extensions, plugin_name):
515
505
  #
516
506
 
517
507
 
518
- # 2.2 TODO: remove in favor of NavMenuItem
519
- @class_deprecated_in_favor_of(NavMenuItem)
520
- class PluginMenuItem:
521
- """
522
- This class represents a navigation menu item. This constitutes primary link and its text, but also allows for
523
- specifying additional link buttons that appear to the right of the item in the van menu.
524
-
525
- Links are specified as Django reverse URL strings.
526
- Buttons are each specified as a list of PluginMenuButton instances.
527
- """
528
-
529
- permissions = []
530
- buttons = []
531
-
532
- def __init__(self, link, link_text, permissions=None, buttons=None):
533
- self.link = link
534
- self.link_text = link_text
535
- if permissions is not None:
536
- if not isinstance(permissions, (list, tuple)):
537
- raise TypeError("Permissions must be passed as a tuple or list.")
538
- self.permissions = permissions
539
- if buttons is not None:
540
- if not isinstance(buttons, (list, tuple)):
541
- raise TypeError("Buttons must be passed as a tuple or list.")
542
- self.buttons = buttons
543
-
544
-
545
- # 2.2 TODO: remove in favor of NavMenuButton
546
- @class_deprecated_in_favor_of(NavMenuButton)
547
- class PluginMenuButton:
548
- """
549
- This class represents a button within a PluginMenuItem. Note that button colors should come from
550
- ButtonColorChoices.
551
- """
552
-
553
- color = ButtonColorChoices.DEFAULT
554
- permissions = []
555
-
556
- def __init__(self, link, title, icon_class, color=None, permissions=None):
557
- self.link = link
558
- self.title = title
559
- self.icon_class = icon_class
560
- if permissions is not None:
561
- if not isinstance(permissions, (list, tuple)):
562
- raise TypeError("Permissions must be passed as a tuple or list.")
563
- self.permissions = permissions
564
- if color is not None:
565
- if color not in ButtonColorChoices.values():
566
- raise ValueError("Button color must be a choice within ButtonColorChoices.")
567
- self.color = color
568
-
569
-
570
508
  def register_plugin_menu_items(section_name, menu_items):
571
509
  """
572
- Register a list of PluginMenuItem instances for a given menu section (e.g. plugin name)
510
+ Register a list of NavMenuTab instances for a given menu section (e.g. plugin name)
573
511
  """
574
- new_menu_items = []
575
- new_menu_item_weight = 100
576
-
577
512
  nav_menu_items = set()
578
513
 
579
- permissions = set()
580
-
581
514
  for menu_item in menu_items:
582
- if isinstance(menu_item, PluginMenuItem):
583
- # translate old-style plugin menu definitions into the new nav-menu items and buttons
584
-
585
- new_menu_button_weight = 100
586
- new_menu_buttons = []
587
- for button in menu_item.buttons:
588
- new_menu_buttons.append(
589
- NavMenuButton(
590
- link=button.link,
591
- title=button.title,
592
- icon_class=button.icon_class,
593
- button_class=button.color,
594
- permissions=button.permissions,
595
- weight=new_menu_button_weight,
596
- )
597
- )
598
- new_menu_button_weight += 100
599
-
600
- new_menu_items.append(
601
- NavMenuItem(
602
- link=menu_item.link,
603
- name=menu_item.link_text,
604
- permissions=menu_item.permissions,
605
- weight=new_menu_item_weight,
606
- buttons=new_menu_buttons,
607
- )
608
- )
609
- new_menu_item_weight += 100
610
- permissions = permissions.union(menu_item.permissions)
611
- elif isinstance(menu_item, NavMenuTab):
515
+ if isinstance(menu_item, NavMenuTab):
612
516
  nav_menu_items.add(menu_item)
613
517
  else:
614
- raise TypeError("Top level objects need to be an instance of NavMenuTab or PluginMenuItem: {menu_tab}")
615
-
616
- if new_menu_items:
617
- # wrap bare item/button list into the default "Plugins" menu tab and appropriate grouping
618
- if registry["nav_menu"]["tabs"].get("Plugins"):
619
- weight = (
620
- registry["nav_menu"]["tabs"]["Plugins"]["groups"][
621
- list(registry["nav_menu"]["tabs"]["Plugins"]["groups"])[-1]
622
- ]["weight"]
623
- + 100
624
- )
625
- else:
626
- weight = 100
627
- nav_menu_items.add(
628
- NavMenuTab(
629
- name="Plugins",
630
- weight=5000,
631
- # Permissions cast to tuple to match development pattern.
632
- permissions=tuple(permissions),
633
- groups=(NavMenuGroup(name=section_name, weight=weight, items=new_menu_items),),
634
- ),
635
- )
518
+ raise TypeError(f"Top level objects need to be an instance of NavMenuTab: {menu_item}")
636
519
 
637
520
  register_menu_items(nav_menu_items)
638
521
 
@@ -103,9 +103,6 @@ def load_plugin(plugin_name, settings):
103
103
  if middleware not in settings.MIDDLEWARE:
104
104
  settings.MIDDLEWARE.append(middleware)
105
105
 
106
- # Update caching configg
107
- settings.CACHEOPS.update({f"{plugin_name}.{key}": value for key, value in plugin_config.caching_config.items()})
108
-
109
106
  # Update Constance Config and Constance Fieldset
110
107
  if plugin_config.constance_config:
111
108
  app_config = {}
@@ -1,6 +1,6 @@
1
1
  from functools import wraps
2
2
 
3
- from django.contrib.contenttypes.models import ContentType
3
+ from django.apps import apps
4
4
 
5
5
  from nautobot.extras.plugins import CustomValidator
6
6
  from nautobot.extras.registry import registry
@@ -40,6 +40,7 @@ def wrap_model_clean_methods():
40
40
  """
41
41
  Helper function that wraps plugin model validator registered clean methods for all applicable models
42
42
  """
43
- for model in ContentType.objects.filter(FeatureQuery("custom_validators").get_query()):
44
- model_class = model.model_class()
45
- model_class.clean = custom_validator_clean(model_class.clean)
43
+ for app_label, models in FeatureQuery("custom_validators").as_dict():
44
+ for model in models:
45
+ model_class = apps.get_model(app_label=app_label, model_name=model)
46
+ model_class.clean = custom_validator_clean(model_class.clean)
@@ -88,7 +88,7 @@ class InstalledPluginDetailView(LoginRequiredMixin, View):
88
88
 
89
89
  class InstalledPluginsAPIView(NautobotAPIVersionMixin, APIView):
90
90
  """
91
- API view for listing all installed plugins
91
+ API view for listing all installed non-core Apps.
92
92
  """
93
93
 
94
94
  permission_classes = [permissions.IsAdminUser]
@@ -99,15 +99,28 @@ class InstalledPluginsAPIView(NautobotAPIVersionMixin, APIView):
99
99
 
100
100
  @staticmethod
101
101
  def _get_plugin_data(plugin_app_config):
102
+ try:
103
+ home_url = reverse(plugin_app_config.home_view_name)
104
+ except NoReverseMatch:
105
+ home_url = None
106
+ try:
107
+ config_url = reverse(plugin_app_config.config_view_name)
108
+ except NoReverseMatch:
109
+ config_url = None
110
+ try:
111
+ docs_url = reverse(plugin_app_config.docs_view_name)
112
+ except NoReverseMatch:
113
+ docs_url = None
102
114
  return {
103
115
  "name": plugin_app_config.verbose_name,
104
116
  "package": plugin_app_config.name,
105
117
  "author": plugin_app_config.author,
106
118
  "author_email": plugin_app_config.author_email,
107
119
  "description": plugin_app_config.description,
108
- # 2.0 TODO: Remove verison key/value when bumping to major revision
109
- "verison": plugin_app_config.version,
110
120
  "version": plugin_app_config.version,
121
+ "home_url": home_url,
122
+ "config_url": config_url,
123
+ "docs_url": docs_url,
111
124
  }
112
125
 
113
126
  @extend_schema(exclude=True)
@@ -185,18 +185,12 @@ class JobQuerySet(RestrictedQuerySet):
185
185
 
186
186
  def get_for_class_path(self, class_path):
187
187
  try:
188
- source, module_name, job_class_name = class_path.split("/")
189
- repository_slug = None
190
- if source.startswith("git."):
191
- repository_slug = source[4:]
192
- source = "git"
188
+ module_name, job_class_name = class_path.rsplit(".", 1)
193
189
  except ValueError: # not a class_path perhaps?
194
190
  raise self.model.DoesNotExist()
195
191
  return self.get(
196
- source=source,
197
192
  module_name=module_name,
198
193
  job_class_name=job_class_name,
199
- git_repository__slug=repository_slug,
200
194
  )
201
195
 
202
196
 
@@ -51,6 +51,9 @@ class DatasourceContent:
51
51
  self.callback = callback
52
52
  self.weight = weight
53
53
 
54
+ def __repr__(self):
55
+ return f"<{self.__class__.__name__}: {self.content_identifier}>"
56
+
54
57
 
55
58
  def register_datasource_contents(datasource_contents_list):
56
59
  """
@@ -1,11 +1,9 @@
1
1
  import os
2
2
  import random
3
3
  import shutil
4
- import uuid
5
4
  import logging
6
5
  from datetime import timedelta
7
6
 
8
- from cacheops.signals import cache_invalidated, cache_read
9
7
  from django.contrib.contenttypes.models import ContentType
10
8
  from django.core.exceptions import ValidationError
11
9
  from django.db import transaction
@@ -13,8 +11,8 @@ from django.db.models.signals import m2m_changed, pre_delete, pre_save
13
11
  from django.dispatch import receiver
14
12
  from django.utils import timezone
15
13
  from django_prometheus.models import model_deletes, model_inserts, model_updates
16
- from prometheus_client import Counter
17
14
 
15
+ from nautobot.core.celery import app, import_jobs_as_celery_tasks
18
16
  from nautobot.core.utils.config import get_settings_or_config
19
17
  from nautobot.extras.tasks import delete_custom_field_data, provision_field
20
18
  from nautobot.extras.utils import refresh_job_model_from_job_class
@@ -155,30 +153,6 @@ def handle_cf_removed_obj_types(instance, action, pk_set, **kwargs):
155
153
  m2m_changed.connect(handle_cf_removed_obj_types, sender=CustomField.content_types.through)
156
154
 
157
155
 
158
- #
159
- # Caching
160
- #
161
-
162
- cacheops_cache_hit = Counter("cacheops_cache_hit", "Number of cache hits")
163
- cacheops_cache_miss = Counter("cacheops_cache_miss", "Number of cache misses")
164
- cacheops_cache_invalidated = Counter("cacheops_cache_invalidated", "Number of cache invalidations")
165
-
166
-
167
- def cache_read_collector(sender, func, hit, **kwargs):
168
- if hit:
169
- cacheops_cache_hit.inc()
170
- else:
171
- cacheops_cache_miss.inc()
172
-
173
-
174
- def cache_invalidated_collector(sender, obj_dict, **kwargs):
175
- cacheops_cache_invalidated.inc()
176
-
177
-
178
- cache_read.connect(cache_read_collector)
179
- cache_invalidated.connect(cache_invalidated_collector)
180
-
181
-
182
156
  #
183
157
  # Datasources
184
158
  #
@@ -195,11 +169,12 @@ def git_repository_pre_delete(instance, **kwargs):
195
169
  """
196
170
  from nautobot.extras.datasources import refresh_datasource_content
197
171
 
172
+ # FIXME(jathan): In light of jobs overhaul and Git syncs as jobs, we need to rethink this. We
173
+ # might instead make "delete" another Job class and call it here, but also think about how
174
+ # worker events will be such as firing the worker event here.
198
175
  job_result = JobResult.objects.create(
199
176
  name=instance.name,
200
- obj_type=ContentType.objects.get_for_model(instance),
201
177
  user=None,
202
- task_id=uuid.uuid4(),
203
178
  status=JobResultStatusChoices.STATUS_STARTED,
204
179
  )
205
180
 
@@ -210,13 +185,13 @@ def git_repository_pre_delete(instance, **kwargs):
210
185
 
211
186
  refresh_datasource_content("extras.gitrepository", instance, None, job_result, delete=True)
212
187
 
213
- if job_result.status not in JobResultStatusChoices.READY_STATES:
214
- job_result.set_status(JobResultStatusChoices.STATUS_SUCCESS)
215
- job_result.save()
216
-
217
- # TODO(Glenn): In a distributed Nautobot deployment, each Django instance and/or worker instance may have its own clone
188
+ # In a distributed Nautobot deployment, each Django instance and/or worker instance may have its own clone
218
189
  # of this repository; we need some way to ensure that all such clones are deleted.
219
- # For now we just delete the one that we have locally and rely on other methods (notably get_jobs())
190
+ # In the Celery worker case, we can broadcast a control message to all workers to do so:
191
+ app.control.broadcast("discard_git_repository", repository_slug=instance.slug)
192
+ # But we don't have an equivalent way to broadcast to any other Django instances.
193
+ # For now we just delete the one that we have locally and rely on other methods,
194
+ # such as the import_jobs_as_celery_tasks() signal that runs on server startup,
220
195
  # to clean up other clones as they're encountered.
221
196
  if os.path.isdir(instance.filesystem_path):
222
197
  shutil.rmtree(instance.filesystem_path)
@@ -262,40 +237,31 @@ def refresh_job_models(sender, *, apps, **kwargs):
262
237
  """
263
238
  Callback for the nautobot_database_ready signal; updates Jobs in the database based on Job source file availability.
264
239
  """
240
+ from nautobot.extras.jobs import Job as JobClass # avoid circular import
241
+
265
242
  Job = apps.get_model("extras", "Job")
266
- GitRepository = apps.get_model("extras", "GitRepository") # pylint: disable=redefined-outer-name
267
243
 
268
244
  # To make reverse migrations safe
269
- if not hasattr(Job, "job_class_name") or not hasattr(Job, "git_repository"):
245
+ if not hasattr(Job, "job_class_name"):
270
246
  logger.info("Skipping refresh_job_models() as it appears Job model has not yet been migrated to latest.")
271
247
  return
272
248
 
273
- from nautobot.extras.jobs import get_jobs
249
+ import_jobs_as_celery_tasks(app)
274
250
 
275
- # TODO(Glenn): eventually this should be inverted so that get_jobs() relies on the database models...
276
- job_classes = get_jobs()
277
251
  job_models = []
278
- for source, modules in job_classes.items():
279
- git_repository = None
280
- if source.startswith("git."):
281
- try:
282
- git_repository = GitRepository.objects.get(slug=source[4:])
283
- except GitRepository.DoesNotExist:
284
- logger.warning('GitRepository "%s" not found?', source[4:])
285
- source = "git"
286
-
287
- for module_details in modules.values():
288
- for job_class in module_details["jobs"].values():
289
- job_model, _ = refresh_job_model_from_job_class(Job, source, job_class, git_repository=git_repository)
290
- if job_model is not None:
291
- job_models.append(job_model)
292
-
293
- for job_model in Job.objects.all():
294
- if job_model.installed and job_model not in job_models:
252
+ for task in app.tasks.values():
253
+ # Skip Celery tasks that aren't Jobs
254
+ if not isinstance(task, JobClass):
255
+ continue
256
+
257
+ job_model, _ = refresh_job_model_from_job_class(Job, task.__class__)
258
+ if job_model is not None:
259
+ job_models.append(job_model)
260
+
261
+ for job_model in Job.objects.filter(installed=True):
262
+ if job_model not in job_models:
295
263
  logger.info(
296
- "Job %s%s/%s/%s is no longer installed",
297
- job_model.source,
298
- f"/{job_model.git_repository.slug}" if job_model.git_repository is not None else "",
264
+ "Job %s/%s is no longer installed",
299
265
  job_model.module_name,
300
266
  job_model.job_class_name,
301
267
  )
nautobot/extras/tables.py CHANGED
@@ -69,11 +69,11 @@ class="label label-{% if entry.content_identifier in record.provided_contents %}
69
69
  """
70
70
 
71
71
  GITREPOSITORY_BUTTONS = """
72
- <button data-url="{% url 'extras:gitrepository_sync' slug=record.slug %}" type="submit" class="btn btn-primary btn-xs sync-repository" title="Sync" {% if not perms.extras.change_gitrepository %}disabled="disabled"{% endif %}><i class="mdi mdi-source-branch-sync" aria-hidden="true"></i></button>
72
+ <button data-url="{% url 'extras:gitrepository_sync' pk=record.pk %}" type="submit" class="btn btn-primary btn-xs sync-repository" title="Sync" {% if not perms.extras.change_gitrepository %}disabled="disabled"{% endif %}><i class="mdi mdi-source-branch-sync" aria-hidden="true"></i></button>
73
73
  """
74
74
 
75
75
  JOB_BUTTONS = """
76
- <a href="{% url 'extras:job_run' slug=record.slug %}" class="btn btn-primary btn-xs" title="Run/Schedule" {% if not perms.extras.run_job or not record.runnable %}disabled="disabled"{% endif %}><i class="mdi mdi-play" aria-hidden="true"></i></a>
76
+ <a href="{% url 'extras:job_run' pk=record.pk %}" class="btn btn-primary btn-xs" title="Run/Schedule" {% if not perms.extras.run_job or not record.runnable %}disabled="disabled"{% endif %}><i class="mdi mdi-play" aria-hidden="true"></i></a>
77
77
  """
78
78
 
79
79
  OBJECTCHANGE_OBJECT = """
@@ -97,7 +97,7 @@ SCHEDULED_JOB_APPROVAL_QUEUE_BUTTONS = """
97
97
  <button type="button"
98
98
  onClick="handleDetailPostAction('{% url 'extras:scheduledjob_approval_request_view' pk=record.pk %}', '_dry_run')"
99
99
  title="Dry Run"
100
- class="btn btn-primary btn-xs"{% if not perms.extras.run_job %} disabled="disabled"{% endif %}>
100
+ class="btn btn-primary btn-xs"{% if not perms.extras.run_job or not record.job_model.supports_dryrun %} disabled="disabled"{% endif %}>
101
101
  <i class="mdi mdi-play"></i>
102
102
  </button>
103
103
  <button type="button"
@@ -124,7 +124,7 @@ class ComputedFieldTable(BaseTable):
124
124
  fields = (
125
125
  "pk",
126
126
  "label",
127
- "slug",
127
+ "key",
128
128
  "content_type",
129
129
  "description",
130
130
  "weight",
@@ -132,7 +132,7 @@ class ComputedFieldTable(BaseTable):
132
132
  default_columns = (
133
133
  "pk",
134
134
  "label",
135
- "slug",
135
+ "key",
136
136
  "content_type",
137
137
  "description",
138
138
  )
@@ -169,7 +169,7 @@ class ConfigContextSchemaTable(BaseTable):
169
169
  pk = ToggleColumn()
170
170
  name = tables.LinkColumn()
171
171
  owner = tables.LinkColumn()
172
- actions = ButtonsColumn(ConfigContextSchema, pk_field="slug")
172
+ actions = ButtonsColumn(ConfigContextSchema)
173
173
 
174
174
  class Meta(BaseTable.Meta):
175
175
  model = ConfigContextSchema
@@ -298,7 +298,7 @@ class DynamicGroupMembershipTable(DynamicGroupTable):
298
298
  """Hybrid table for displaying info for both group and membership."""
299
299
 
300
300
  description = tables.Column(accessor="group.description")
301
- actions = ButtonsColumn(DynamicGroup, pk_field="slug", buttons=("edit",))
301
+ actions = ButtonsColumn(DynamicGroup, pk_field="pk", buttons=("edit",))
302
302
 
303
303
  class Meta(BaseTable.Meta):
304
304
  model = DynamicGroupMembership
@@ -421,15 +421,15 @@ class GitRepositoryTable(BaseTable):
421
421
 
422
422
  class JobResultColumn(tables.TemplateColumn):
423
423
  def render(self, record, table, value, bound_column, **kwargs):
424
- if record.name in table.context.get("job_results", {}):
425
- table.context.update({"result": table.context["job_results"][record.name]})
424
+ if str(record.pk) in table.context.get("job_results", {}):
425
+ table.context.update({"result": table.context["job_results"][str(record.pk)]})
426
426
  else:
427
427
  table.context.update({"result": None})
428
428
  return super().render(record, table, value, bound_column, **kwargs)
429
429
 
430
430
  last_sync_status = JobResultColumn(template_name="extras/inc/job_label.html", verbose_name="Sync Status")
431
431
  provides = tables.TemplateColumn(GITREPOSITORY_PROVIDES)
432
- actions = ButtonsColumn(GitRepository, pk_field="slug", prepend_template=GITREPOSITORY_BUTTONS)
432
+ actions = ButtonsColumn(GitRepository, prepend_template=GITREPOSITORY_BUTTONS)
433
433
 
434
434
  class Meta(BaseTable.Meta):
435
435
  model = GitRepository
@@ -518,15 +518,16 @@ class JobTable(BaseTable):
518
518
  enabled = BooleanColumn()
519
519
  has_sensitive_variables = BooleanColumn()
520
520
  description = tables.Column(accessor="description_first_line")
521
- commit_default = BooleanColumn()
521
+ dryrun_default = BooleanColumn()
522
522
  hidden = BooleanColumn()
523
523
  read_only = BooleanColumn()
524
524
  approval_required = BooleanColumn()
525
525
  is_job_hook_receiver = BooleanColumn()
526
526
  is_job_button_receiver = BooleanColumn()
527
+ supports_dryrun = BooleanColumn()
527
528
  soft_time_limit = tables.Column()
528
529
  time_limit = tables.Column()
529
- actions = ButtonsColumn(JobModel, pk_field="slug", prepend_template=JOB_BUTTONS)
530
+ actions = ButtonsColumn(JobModel, prepend_template=JOB_BUTTONS)
530
531
  last_run = tables.TemplateColumn(
531
532
  accessor="latest_result",
532
533
  template_code="""
@@ -556,12 +557,13 @@ class JobTable(BaseTable):
556
557
  "enabled",
557
558
  "has_sensitive_variables",
558
559
  "description",
559
- "commit_default",
560
+ "dryrun_default",
560
561
  "hidden",
561
562
  "read_only",
562
563
  "is_job_hook_receiver",
563
564
  "is_job_button_receiver",
564
565
  "approval_required",
566
+ "supports_dryrun",
565
567
  "soft_time_limit",
566
568
  "time_limit",
567
569
  "last_run",
@@ -621,8 +623,10 @@ class JobLogEntryTable(BaseTable):
621
623
  def render_log_level(self, value):
622
624
  log_level = value.lower()
623
625
  # The css is label-danger for failure items.
624
- if log_level == "failure":
626
+ if log_level in ["error", "critical"]:
625
627
  log_level = "danger"
628
+ elif log_level == "debug":
629
+ log_level = "default"
626
630
 
627
631
  return format_html('<label class="label label-{}">{}</label>', log_level, value)
628
632
 
@@ -644,8 +648,7 @@ class JobLogEntryTable(BaseTable):
644
648
 
645
649
  class JobResultTable(BaseTable):
646
650
  pk = ToggleColumn()
647
- linked_record = tables.Column(verbose_name="Job / Git Repository", linkify=True)
648
- name = tables.Column()
651
+ job_model = tables.Column(linkify=True)
649
652
  date_created = tables.DateTimeColumn(linkify=True, format=settings.SHORT_DATETIME_FORMAT)
650
653
  status = tables.TemplateColumn(
651
654
  template_code="{% include 'extras/inc/job_label.html' with result=record %}",
@@ -661,12 +664,12 @@ class JobResultTable(BaseTable):
661
664
  {% load helpers %}
662
665
  {% if perms.extras.run_job %}
663
666
  {% if record.job_model and record.task_kwargs %}
664
- <a href="{% url 'extras:job_run' slug=record.job_model.slug %}?kwargs_from_job_result={{ record.pk }}"
667
+ <a href="{% url 'extras:job_run' pk=record.job_model.pk %}?kwargs_from_job_result={{ record.pk }}"
665
668
  class="btn btn-xs btn-success" title="Re-run job with same arguments.">
666
669
  <i class="mdi mdi-repeat"></i>
667
670
  </a>
668
671
  {% elif record.job_model is not None %}
669
- <a href="{% url 'extras:job_run' slug=record.job_model.slug %}" class="btn btn-primary btn-xs"
672
+ <a href="{% url 'extras:job_run' pk=record.job_model.pk %}" class="btn btn-primary btn-xs"
670
673
  title="Run job">
671
674
  <i class="mdi mdi-play"></i>
672
675
  </a>
@@ -683,33 +686,24 @@ class JobResultTable(BaseTable):
683
686
  """
684
687
  )
685
688
 
686
- def order_linked_record(self, queryset, is_descending):
687
- return (
688
- queryset.order_by(
689
- ("-" if is_descending else "") + "job_model__name",
690
- ("-" if is_descending else "") + "name",
691
- ),
692
- True,
693
- )
694
-
695
689
  def render_summary(self, record):
696
690
  """
697
691
  Define custom rendering for the summary column.
698
692
  """
699
693
  log_objects = record.job_log_entries.all()
700
- success = log_objects.filter(log_level=LogLevelChoices.LOG_SUCCESS).count()
694
+ debug = log_objects.filter(log_level=LogLevelChoices.LOG_DEBUG).count()
701
695
  info = log_objects.filter(log_level=LogLevelChoices.LOG_INFO).count()
702
696
  warning = log_objects.filter(log_level=LogLevelChoices.LOG_WARNING).count()
703
- failure = log_objects.filter(log_level=LogLevelChoices.LOG_FAILURE).count()
697
+ error = log_objects.filter(log_level__in=[LogLevelChoices.LOG_ERROR, LogLevelChoices.LOG_CRITICAL]).count()
704
698
  return format_html(
705
- """<label class="label label-success">{}</label>
699
+ """<label class="label label-default">{}</label>
706
700
  <label class="label label-info">{}</label>
707
701
  <label class="label label-warning">{}</label>
708
702
  <label class="label label-danger">{}</label>""",
709
- success,
703
+ debug,
710
704
  info,
711
705
  warning,
712
- failure,
706
+ error,
713
707
  )
714
708
 
715
709
  class Meta(BaseTable.Meta):
@@ -718,7 +712,7 @@ class JobResultTable(BaseTable):
718
712
  "pk",
719
713
  "date_created",
720
714
  "name",
721
- "linked_record",
715
+ "job_model",
722
716
  "duration",
723
717
  "date_done",
724
718
  "user",
@@ -726,7 +720,7 @@ class JobResultTable(BaseTable):
726
720
  "summary",
727
721
  "actions",
728
722
  )
729
- default_columns = ("pk", "date_created", "name", "linked_record", "user", "status", "summary", "actions")
723
+ default_columns = ("pk", "date_created", "name", "job_model", "user", "status", "summary", "actions")
730
724
 
731
725
 
732
726
  class JobButtonTable(BaseTable):
@@ -766,7 +760,7 @@ class JobButtonTable(BaseTable):
766
760
 
767
761
 
768
762
  class NoteTable(BaseTable):
769
- actions = ButtonsColumn(Note, pk_field="slug")
763
+ actions = ButtonsColumn(Note)
770
764
 
771
765
  class Meta(BaseTable.Meta):
772
766
  model = Note
@@ -834,14 +828,14 @@ class ObjectChangeTable(BaseTable):
834
828
 
835
829
  class RelationshipTable(BaseTable):
836
830
  pk = ToggleColumn()
837
- name = tables.Column(linkify=True)
838
- actions = ButtonsColumn(Relationship, pk_field="slug", buttons=("edit", "delete"))
831
+ label = tables.Column(linkify=True)
832
+ actions = ButtonsColumn(Relationship, buttons=("edit", "delete"))
839
833
 
840
834
  class Meta(BaseTable.Meta):
841
835
  model = Relationship
842
836
  fields = (
843
837
  "pk",
844
- "name",
838
+ "label",
845
839
  "description",
846
840
  "type",
847
841
  "source_type",
@@ -973,10 +967,10 @@ class StatusTableMixin(BaseTable):
973
967
 
974
968
  class TagTable(BaseTable):
975
969
  pk = ToggleColumn()
976
- name = tables.LinkColumn(viewname="extras:tag", args=[Accessor("slug")])
970
+ name = tables.LinkColumn(viewname="extras:tag", args=[Accessor("pk")])
977
971
  color = ColorColumn()
978
972
  content_types = ContentTypesColumn(truncate_words=15)
979
- actions = ButtonsColumn(Tag, pk_field="slug")
973
+ actions = ButtonsColumn(Tag)
980
974
 
981
975
  class Meta(BaseTable.Meta):
982
976
  model = Tag
nautobot/extras/tasks.py CHANGED
@@ -171,15 +171,3 @@ def process_webhook(webhook_pk, data, model_name, event, timestamp, username, re
171
171
  raise requests.exceptions.RequestException(
172
172
  f"Status {response.status_code} returned with content '{response.content}', webhook FAILED to process."
173
173
  )
174
-
175
-
176
- # TODO(jathan): Delete these two functions before we ship this. These are handy
177
- # for Jobs v2 testing for now.
178
- @nautobot_task
179
- def add(x, y):
180
- return x + y
181
-
182
-
183
- @nautobot_task
184
- def fail():
185
- raise Exception("You have failed.") # pylint: disable=broad-exception-raised