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
@@ -1,4 +1,4 @@
1
- from unittest import skipIf, skip
1
+ from unittest import skip, skipIf
2
2
 
3
3
  from django.db import connection
4
4
 
@@ -11,9 +11,10 @@ from nautobot.extras.choices import CustomFieldTypeChoices
11
11
  connection.vendor != "postgresql",
12
12
  "mysql does not support rollbacks",
13
13
  )
14
+ @skip("test skipped until base test can be fixed to handle new migrations")
14
15
  class CustomFieldDataMigrationTest(NautobotDataMigrationTest):
15
- migrate_from = [("extras", "0074_rename_slug_to_key_for_custom_field")]
16
- migrate_to = [("extras", "0075_migrate_custom_field_data")]
16
+ migrate_from = [("extras", "0075_rename_slug_to_key_for_custom_field")]
17
+ migrate_to = [("extras", "0076_migrate_custom_field_data")]
17
18
 
18
19
  def populateDataBeforeMigration(self, installed_apps):
19
20
  """populate CustomField data pre-migrations"""
@@ -1,8 +1,7 @@
1
1
  import os
2
2
  import tempfile
3
- import datetime
4
- from unittest import mock, skip
5
- import uuid
3
+ from unittest import mock
4
+ import warnings
6
5
 
7
6
  from django.conf import settings
8
7
  from django.contrib.contenttypes.models import ContentType
@@ -11,7 +10,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
11
10
  from django.db.models import ProtectedError
12
11
  from django.db.utils import IntegrityError
13
12
  from django.test import override_settings
14
-
13
+ from django.test.utils import isolate_apps
15
14
 
16
15
  from nautobot.core.choices import ColorChoices
17
16
  from nautobot.core.testing import TestCase, TransactionTestCase
@@ -26,7 +25,7 @@ from nautobot.dcim.models import (
26
25
  )
27
26
  from nautobot.extras.constants import JOB_OVERRIDABLE_FIELDS
28
27
  from nautobot.extras.choices import LogLevelChoices, SecretsGroupAccessTypeChoices, SecretsGroupSecretTypeChoices
29
- from nautobot.extras.jobs import get_job, Job as JobClass
28
+ from nautobot.extras.jobs import get_job
30
29
  from nautobot.extras.models import (
31
30
  ComputedField,
32
31
  ConfigContext,
@@ -47,9 +46,9 @@ from nautobot.extras.models import (
47
46
  Tag,
48
47
  Webhook,
49
48
  )
50
- from nautobot.extras.utils import get_job_content_type
49
+ from nautobot.extras.models.statuses import StatusModel
51
50
  from nautobot.extras.secrets.exceptions import SecretParametersError, SecretProviderError, SecretValueNotFoundError
52
- from nautobot.ipam.models import IPAddress, Prefix, Namespace
51
+ from nautobot.ipam.models import IPAddress
53
52
  from nautobot.tenancy.models import Tenant
54
53
  from nautobot.virtualization.models import (
55
54
  Cluster,
@@ -59,7 +58,6 @@ from nautobot.virtualization.models import (
59
58
  )
60
59
 
61
60
 
62
- @skip(reason="Content Types are BROKEN")
63
61
  class ComputedFieldTest(ModelTestCases.BaseModelTestCase):
64
62
  """
65
63
  Tests for the `ComputedField` Model
@@ -70,7 +68,7 @@ class ComputedFieldTest(ModelTestCases.BaseModelTestCase):
70
68
  def setUp(self):
71
69
  self.good_computed_field = ComputedField.objects.create(
72
70
  content_type=ContentType.objects.get_for_model(Location),
73
- slug="good_computed_field",
71
+ key="good_computed_field",
74
72
  label="Good Computed Field",
75
73
  template="{{ obj.name }} is awesome!",
76
74
  fallback_value="This template has errored",
@@ -78,7 +76,7 @@ class ComputedFieldTest(ModelTestCases.BaseModelTestCase):
78
76
  )
79
77
  self.bad_computed_field = ComputedField.objects.create(
80
78
  content_type=ContentType.objects.get_for_model(Location),
81
- slug="bad_computed_field",
79
+ key="bad_computed_field",
82
80
  label="Bad Computed Field",
83
81
  template="{{ not_in_context | not_a_filter }} is horrible!",
84
82
  fallback_value="An error occurred while rendering this template.",
@@ -86,7 +84,7 @@ class ComputedFieldTest(ModelTestCases.BaseModelTestCase):
86
84
  )
87
85
  self.blank_fallback_value = ComputedField.objects.create(
88
86
  content_type=ContentType.objects.get_for_model(Location),
89
- slug="blank_fallback_value",
87
+ key="blank_fallback_value",
90
88
  label="Blank Fallback Value",
91
89
  template="{{ obj.location }}",
92
90
  weight=50,
@@ -105,6 +103,47 @@ class ComputedFieldTest(ModelTestCases.BaseModelTestCase):
105
103
  rendered_value = self.bad_computed_field.render(context={"obj": self.location1})
106
104
  self.assertEqual(rendered_value, self.bad_computed_field.fallback_value)
107
105
 
106
+ def test_check_if_key_is_graphql_safe(self):
107
+ """
108
+ Check the GraphQL validation method on CustomField Key Attribute.
109
+ """
110
+ # Check if it catches the cpf.key starting with a digit.
111
+ cpf1 = ComputedField(
112
+ label="Test 1",
113
+ key="12_test_1",
114
+ content_type=ContentType.objects.get_for_model(Device),
115
+ )
116
+ with self.assertRaises(ValidationError) as error:
117
+ cpf1.validated_save()
118
+ self.assertIn(
119
+ "This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
120
+ str(error.exception),
121
+ )
122
+ # Check if it catches the cpf.key with whitespace.
123
+ cpf1.key = "test 1"
124
+ with self.assertRaises(ValidationError) as error:
125
+ cpf1.validated_save()
126
+ self.assertIn(
127
+ "This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
128
+ str(error.exception),
129
+ )
130
+ # Check if it catches the cpf.key with hyphens.
131
+ cpf1.key = "test-1-computed-field"
132
+ with self.assertRaises(ValidationError) as error:
133
+ cpf1.validated_save()
134
+ self.assertIn(
135
+ "This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
136
+ str(error.exception),
137
+ )
138
+ # Check if it catches the cpf.key with special characters
139
+ cpf1.key = "test_1_computed_f)(&d"
140
+ with self.assertRaises(ValidationError) as error:
141
+ cpf1.validated_save()
142
+ self.assertIn(
143
+ "This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
144
+ str(error.exception),
145
+ )
146
+
108
147
 
109
148
  class ConfigContextTest(ModelTestCases.BaseModelTestCase):
110
149
  """
@@ -115,39 +154,43 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
115
154
 
116
155
  model = ConfigContext
117
156
 
118
- def setUp(self):
157
+ @classmethod
158
+ def setUpTestData(cls):
119
159
  manufacturer = Manufacturer.objects.first()
120
- self.devicetype = DeviceType.objects.create(
160
+ cls.devicetype = DeviceType.objects.create(
121
161
  manufacturer=manufacturer, model="Device Type 1", slug="device-type-1"
122
162
  )
123
- self.devicerole = Role.objects.get_for_model(Device).first()
163
+ cls.devicerole = Role.objects.get_for_model(Device).first()
124
164
  location_type = LocationType.objects.create(name="Location Type 1")
125
- self.location = Location.objects.create(name="Location 1", location_type=location_type)
126
- self.platform = Platform.objects.first()
127
- self.tenant = Tenant.objects.first()
128
- self.tenantgroup = self.tenant.tenant_group
129
- self.tag, self.tag2 = Tag.objects.get_for_model(Device)[:2]
130
- self.dynamic_groups = DynamicGroup.objects.create(
165
+ location_status = Status.objects.get_for_model(Location).first()
166
+ cls.location = Location.objects.create(name="Location 1", location_type=location_type, status=location_status)
167
+ cls.platform = Platform.objects.first()
168
+ cls.tenant = Tenant.objects.first()
169
+ cls.tenantgroup = cls.tenant.tenant_group
170
+ cls.tag, cls.tag2 = Tag.objects.get_for_model(Device)[:2]
171
+ cls.dynamic_groups = DynamicGroup.objects.create(
131
172
  name="Dynamic Group",
132
173
  content_type=ContentType.objects.get_for_model(Device),
133
174
  filter={"name": ["Device 1", "Device 2"]},
134
175
  )
135
- self.dynamic_group_2 = DynamicGroup.objects.create(
176
+ cls.dynamic_group_2 = DynamicGroup.objects.create(
136
177
  name="Dynamic Group 2",
137
178
  content_type=ContentType.objects.get_for_model(Device),
138
179
  filter={"name": ["Device 2"]},
139
180
  )
140
- self.vm_dynamic_group = DynamicGroup.objects.create(
181
+ cls.vm_dynamic_group = DynamicGroup.objects.create(
141
182
  name="VM Dynamic Group",
142
183
  content_type=ContentType.objects.get_for_model(VirtualMachine),
143
184
  filter={"name": ["VM 1"]},
144
185
  )
145
186
 
146
- self.device = Device.objects.create(
187
+ cls.device_status = Status.objects.get_for_model(Device).first()
188
+ cls.device = Device.objects.create(
147
189
  name="Device 1",
148
- device_type=self.devicetype,
149
- role=self.devicerole,
150
- location=self.location,
190
+ device_type=cls.devicetype,
191
+ role=cls.devicerole,
192
+ location=cls.location,
193
+ status=cls.device_status,
151
194
  )
152
195
 
153
196
  ConfigContext.objects.create(name="context 1", weight=100, data={"a": 123, "b": 456, "c": 777})
@@ -178,7 +221,7 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
178
221
  slug="test-git-repo",
179
222
  remote_url="http://localhost/git.git",
180
223
  )
181
- repo.save(trigger_resync=False)
224
+ repo.save()
182
225
 
183
226
  nonduplicate_context = ConfigContext(name="context 1", weight=300, data={"a": "22"}, owner=repo)
184
227
  nonduplicate_context.validated_save()
@@ -217,6 +260,7 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
217
260
  tenant=self.tenant,
218
261
  platform=self.platform,
219
262
  role=self.devicerole,
263
+ status=self.device_status,
220
264
  device_type=self.devicetype,
221
265
  )
222
266
  device.tags.add(self.tag)
@@ -257,12 +301,14 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
257
301
  )
258
302
  dynamic_group_context.dynamic_groups.add(self.vm_dynamic_group)
259
303
 
304
+ vm_status = Status.objects.get_for_model(VirtualMachine).first()
260
305
  virtual_machine = VirtualMachine.objects.create(
261
306
  name="VM 1",
262
307
  cluster=cluster,
263
308
  tenant=self.tenant,
264
309
  platform=self.platform,
265
310
  role=self.devicerole,
311
+ status=vm_status,
266
312
  )
267
313
  virtual_machine.tags.add(self.tag)
268
314
 
@@ -301,6 +347,7 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
301
347
  tenant=self.tenant,
302
348
  platform=self.platform,
303
349
  role=self.devicerole,
350
+ status=self.device_status,
304
351
  device_type=self.devicetype,
305
352
  )
306
353
  device.tags.add(self.tag)
@@ -332,6 +379,7 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
332
379
  tenant=self.tenant,
333
380
  platform=self.platform,
334
381
  role=self.devicerole,
382
+ status=self.device_status,
335
383
  device_type=self.devicetype,
336
384
  )
337
385
  device.tags.add(self.tag)
@@ -355,6 +403,7 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
355
403
  tenant=self.tenant,
356
404
  platform=self.platform,
357
405
  role=self.devicerole,
406
+ status=self.device_status,
358
407
  device_type=self.devicetype,
359
408
  )
360
409
  dynamic_group_context = ConfigContext.objects.create(
@@ -650,7 +699,7 @@ class ExportTemplateTest(ModelTestCases.BaseModelTestCase):
650
699
  slug="test-git-repo",
651
700
  remote_url="http://localhost/git.git",
652
701
  )
653
- repo.save(trigger_resync=False)
702
+ repo.save()
654
703
  nonduplicate_template = ExportTemplate(
655
704
  content_type=self.device_ct, name="Export Template 1", owner=repo, template_code="bar"
656
705
  )
@@ -701,26 +750,11 @@ class GitRepositoryTest(TransactionTestCase): # TODO: BaseModelTestCase mixin?
701
750
  slug="test-git-repo",
702
751
  remote_url="http://localhost/git.git",
703
752
  )
704
- self.repo.save(trigger_resync=False)
753
+ self.repo.save()
705
754
 
706
755
  def test_filesystem_path(self):
707
756
  self.assertEqual(self.repo.filesystem_path, os.path.join(settings.GIT_ROOT, self.repo.slug))
708
757
 
709
- def test_save_relocate_directory(self):
710
- with tempfile.TemporaryDirectory() as tmpdirname:
711
- with self.settings(GIT_ROOT=tmpdirname):
712
- initial_path = self.repo.filesystem_path
713
- self.assertIn(self.repo.slug, initial_path)
714
- os.makedirs(initial_path)
715
-
716
- self.repo.slug = "a-new-location"
717
- self.repo.save(trigger_resync=False)
718
-
719
- self.assertFalse(os.path.exists(initial_path))
720
- new_path = self.repo.filesystem_path
721
- self.assertIn(self.repo.slug, new_path)
722
- self.assertTrue(os.path.isdir(new_path))
723
-
724
758
 
725
759
  class JobModelTest(ModelTestCases.BaseModelTestCase):
726
760
  """
@@ -744,10 +778,10 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
744
778
  self.assertEqual(self.plugin_job.job_class, ExampleJob)
745
779
 
746
780
  def test_class_path(self):
747
- self.assertEqual(self.local_job.class_path, "local/test_pass/TestPass")
781
+ self.assertEqual(self.local_job.class_path, "pass.TestPass")
748
782
  self.assertEqual(self.local_job.class_path, self.local_job.job_class.class_path)
749
783
 
750
- self.assertEqual(self.plugin_job.class_path, "plugins/example_plugin.jobs/ExampleJob")
784
+ self.assertEqual(self.plugin_job.class_path, "example_plugin.jobs.ExampleJob")
751
785
  self.assertEqual(self.plugin_job.class_path, self.plugin_job.job_class.class_path)
752
786
 
753
787
  def test_latest_result(self):
@@ -759,13 +793,24 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
759
793
  """Verify that defaults for discovered JobModel instances are as expected."""
760
794
  for job_model in JobModel.objects.all():
761
795
  self.assertTrue(job_model.installed)
762
- self.assertFalse(job_model.enabled)
796
+ # System jobs should be enabled by default, all others are disabled by default
797
+ if job_model.module_name.startswith("nautobot."):
798
+ self.assertTrue(job_model.enabled)
799
+ else:
800
+ self.assertFalse(job_model.enabled)
763
801
  for field_name in JOB_OVERRIDABLE_FIELDS:
764
- if field_name == "name" and "test_duplicate_name" in job_model.job_class.__module__:
802
+ if field_name == "name" and "duplicate_name" in job_model.job_class.__module__:
765
803
  pass # name field for test_duplicate_name jobs tested in test_duplicate_job_name below
766
804
  else:
767
- self.assertFalse(getattr(job_model, f"{field_name}_override"))
768
- self.assertEqual(getattr(job_model, field_name), getattr(job_model.job_class, field_name))
805
+ self.assertFalse(
806
+ getattr(job_model, f"{field_name}_override"),
807
+ (field_name, getattr(job_model, field_name), getattr(job_model.job_class, field_name)),
808
+ )
809
+ self.assertEqual(
810
+ getattr(job_model, field_name),
811
+ getattr(job_model.job_class, field_name),
812
+ field_name,
813
+ )
769
814
 
770
815
  def test_duplicate_job_name(self):
771
816
  self.assertTrue(JobModel.objects.filter(name="TestDuplicateNameNoMeta").exists())
@@ -780,9 +825,8 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
780
825
  "grouping": "Overridden Grouping",
781
826
  "name": "Overridden Name",
782
827
  "description": "Overridden Description",
783
- "commit_default": not self.job_containing_sensitive_variables.commit_default,
828
+ "dryrun_default": not self.job_containing_sensitive_variables.dryrun_default,
784
829
  "hidden": not self.job_containing_sensitive_variables.hidden,
785
- "read_only": not self.job_containing_sensitive_variables.read_only,
786
830
  "approval_required": not self.job_containing_sensitive_variables.approval_required,
787
831
  "has_sensitive_variables": not self.job_containing_sensitive_variables.has_sensitive_variables,
788
832
  "soft_time_limit": 350,
@@ -816,7 +860,6 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
816
860
  """Verify that cleaning enforces validation of potentially unsanitized user input."""
817
861
  with self.assertRaises(ValidationError) as handler:
818
862
  JobModel(
819
- source="local",
820
863
  module_name="too_long_of_a_module_name.too_long_of_a_module_name.too_long_of_a_module_name.too_long_of_a_module_name.too_long_of_a_module_name",
821
864
  job_class_name="JobClass",
822
865
  grouping="grouping",
@@ -826,7 +869,6 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
826
869
 
827
870
  with self.assertRaises(ValidationError) as handler:
828
871
  JobModel(
829
- source="local",
830
872
  module_name="module_name",
831
873
  job_class_name="ThisIsARidiculouslyLongJobClassNameWhoWouldEverDoSuchAnUtterlyRidiculousThingButBetterSafeThanSorrySinceWeAreDealingWithUserInputHere",
832
874
  grouping="grouping",
@@ -836,7 +878,6 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
836
878
 
837
879
  with self.assertRaises(ValidationError) as handler:
838
880
  JobModel(
839
- source="local",
840
881
  module_name="module_name",
841
882
  job_class_name="JobClassName",
842
883
  grouping="OK now this is just ridiculous. Why would you ever want to deal with typing in 255+ characters of grouping information and have to copy-paste it to the other jobs in the same grouping or risk dealing with typos when typing out such a ridiculously long grouping string? Still, once again, better safe than sorry!",
@@ -846,7 +887,6 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
846
887
 
847
888
  with self.assertRaises(ValidationError) as handler:
848
889
  JobModel(
849
- source="local",
850
890
  module_name="module_name",
851
891
  job_class_name="JobClassName",
852
892
  grouping="grouping",
@@ -856,7 +896,6 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
856
896
 
857
897
  with self.assertRaises(ValidationError) as handler:
858
898
  JobModel(
859
- source="local",
860
899
  module_name="module_name",
861
900
  job_class_name="JobClassName",
862
901
  grouping="grouping",
@@ -870,67 +909,6 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
870
909
  )
871
910
 
872
911
 
873
- class JobResultTest(TestCase):
874
- """
875
- Tests for the `JobResult` model class.
876
- """
877
-
878
- def test_related_object(self):
879
- """Test that the `related_object` property is computed properly."""
880
- # Case 1: Job, identified by class_path.
881
- job_class = get_job("local/test_pass/TestPass")
882
- job_result = JobResult(
883
- name=job_class.class_path,
884
- obj_type=get_job_content_type(),
885
- task_id=uuid.uuid4(),
886
- )
887
-
888
- # Can't just do self.assertEqual(job_result.related_object, job_class) here for some reason
889
- self.assertEqual(type(job_result.related_object), type)
890
- self.assertTrue(issubclass(job_result.related_object, JobClass))
891
- self.assertEqual(job_result.related_object.class_path, "local/test_pass/TestPass")
892
-
893
- job_result.name = "local/no_such_job/NoSuchJob"
894
- self.assertIsNone(job_result.related_object)
895
-
896
- job_result.name = "not-a-class-path"
897
- self.assertIsNone(job_result.related_object)
898
-
899
- # Case 2: GitRepository, identified by name.
900
- repo = GitRepository(
901
- name="Test Git Repository",
902
- slug="test-git-repo",
903
- remote_url="http://localhost/git.git",
904
- )
905
- repo.save(trigger_resync=False)
906
-
907
- job_result = JobResult(
908
- name=repo.name,
909
- obj_type=ContentType.objects.get_for_model(repo),
910
- task_id=uuid.uuid4(),
911
- )
912
-
913
- self.assertEqual(job_result.related_object, repo)
914
-
915
- job_result.name = "No such GitRepository"
916
- self.assertIsNone(job_result.related_object)
917
-
918
- # Case 3: Related object with no name, identified by PK/ID
919
- namespace = Namespace.objects.first()
920
- Prefix.objects.create(prefix="1.1.1.0/24", namespace=namespace)
921
- ip_address = IPAddress.objects.create(address="1.1.1.1/32", namespace=namespace)
922
- job_result = JobResult(
923
- name="irrelevant",
924
- obj_type=ContentType.objects.get_for_model(ip_address),
925
- task_id=ip_address.pk,
926
- )
927
-
928
- self.assertEqual(job_result.related_object, ip_address)
929
-
930
- job_result.task_id = uuid.uuid4()
931
- self.assertIsNone(job_result.related_object)
932
-
933
-
934
912
  class RoleTest(TestCase):
935
913
  """Tests for `Role` model class."""
936
914
 
@@ -1395,6 +1373,22 @@ class StatusTest(ModelTestCases.BaseModelTestCase):
1395
1373
  self.status.save()
1396
1374
  self.assertEqual(str(self.status), test)
1397
1375
 
1376
+ @isolate_apps("nautobot.extras.tests")
1377
+ def test_deprecated_mixin_class(self):
1378
+ """Test that inheriting from StatusModel raises a DeprecationWarning."""
1379
+ with warnings.catch_warnings(record=True) as warn_list:
1380
+ warnings.simplefilter("always")
1381
+
1382
+ class MyModel(StatusModel): # pylint: disable=unused-variable
1383
+ pass
1384
+
1385
+ self.assertEqual(len(warn_list), 1)
1386
+ warning = warn_list[0]
1387
+ self.assertTrue(issubclass(warning.category, DeprecationWarning))
1388
+ self.assertIn("StatusModel is deprecated", str(warning))
1389
+ self.assertIn("Instead of deriving MyModel from StatusModel", str(warning))
1390
+ self.assertIn("please directly declare `status = StatusField(...)` on your model instead", str(warning))
1391
+
1398
1392
 
1399
1393
  class TagTest(ModelTestCases.BaseModelTestCase):
1400
1394
  model = Tag
@@ -1412,20 +1406,15 @@ class JobLogEntryTest(TestCase): # TODO: change to BaseModelTestCase
1412
1406
  """
1413
1407
 
1414
1408
  def setUp(self):
1415
- module = "test_pass"
1409
+ module = "pass"
1416
1410
  name = "TestPass"
1417
- job_class = get_job(f"local/{module}/{name}")
1411
+ job_class = get_job(f"{module}.{name}")
1418
1412
 
1419
- self.job_result = JobResult.objects.create(
1420
- name=job_class.class_path,
1421
- obj_type=get_job_content_type(),
1422
- user=None,
1423
- task_id=uuid.uuid4(),
1424
- )
1413
+ self.job_result = JobResult.objects.create(name=job_class.class_path, user=None)
1425
1414
 
1426
1415
  def test_log_entry_creation(self):
1427
1416
  log = JobLogEntry(
1428
- log_level=LogLevelChoices.LOG_SUCCESS,
1417
+ log_level=LogLevelChoices.LOG_INFO,
1429
1418
  job_result=self.job_result,
1430
1419
  grouping="run",
1431
1420
  message="This is a test",
@@ -1438,40 +1427,6 @@ class JobLogEntryTest(TestCase): # TODO: change to BaseModelTestCase
1438
1427
  self.assertEqual(log_object.log_level, log.log_level)
1439
1428
  self.assertEqual(log_object.grouping, log.grouping)
1440
1429
 
1441
- def test_to_csv_no_log_object(self):
1442
- """Check that `to_csv` returns the correct data from the JobLogEntry model."""
1443
- expected_data = ("2020-01-26 15:37:36", "run", "success", "", "Django Test")
1444
-
1445
- joblogentry_a = JobLogEntry(
1446
- job_result=self.job_result,
1447
- log_level=LogLevelChoices.LOG_SUCCESS,
1448
- grouping="run",
1449
- message="Django Test",
1450
- created=datetime.datetime(2020, 1, 26, 15, 37, 36),
1451
- log_object="",
1452
- absolute_url="",
1453
- )
1454
- joblogentry_a.validated_save()
1455
- csv_data = joblogentry_a.to_csv()
1456
- self.assertEqual(expected_data, csv_data)
1457
-
1458
- def test_to_csv_with_log_object(self):
1459
- """Check that `to_csv` returns the correct data from the JobLogEntry model."""
1460
- expected_data = ("2030-05-26 15:37:36", "run", "success", "ams01-dist-01", "Django Test 2")
1461
-
1462
- joblogentry_a = JobLogEntry(
1463
- job_result=self.job_result,
1464
- log_level=LogLevelChoices.LOG_SUCCESS,
1465
- grouping="run",
1466
- message="Django Test 2",
1467
- created=datetime.datetime(2030, 5, 26, 15, 37, 36),
1468
- log_object="ams01-dist-01",
1469
- absolute_url="https://nautobot.io/dcim/devices/8d769e14-286a-489c-b705-bd15c476abbb",
1470
- )
1471
- joblogentry_a.validated_save()
1472
- csv_data = joblogentry_a.to_csv()
1473
- self.assertEqual(expected_data, csv_data)
1474
-
1475
1430
 
1476
1431
  class WebhookTest(TestCase): # TODO: change to BaseModelTestCase
1477
1432
  def test_type_error_not_raised_when_calling_check_for_conflicts(self):