nautobot 2.4.0b1__py3-none-any.whl → 2.4.1__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.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (601) hide show
  1. nautobot/apps/__init__.py +1 -1
  2. nautobot/apps/api.py +8 -8
  3. nautobot/apps/change_logging.py +2 -2
  4. nautobot/apps/choices.py +4 -4
  5. nautobot/apps/events.py +3 -3
  6. nautobot/apps/factory.py +2 -2
  7. nautobot/apps/filters.py +1 -1
  8. nautobot/apps/forms.py +20 -20
  9. nautobot/apps/graphql.py +2 -2
  10. nautobot/apps/jobs.py +8 -8
  11. nautobot/apps/models.py +19 -19
  12. nautobot/apps/tables.py +1 -1
  13. nautobot/apps/testing.py +10 -10
  14. nautobot/apps/ui.py +2 -2
  15. nautobot/apps/utils.py +7 -7
  16. nautobot/apps/views.py +7 -7
  17. nautobot/circuits/api/serializers.py +1 -0
  18. nautobot/circuits/api/views.py +4 -8
  19. nautobot/circuits/tables.py +2 -1
  20. nautobot/circuits/templates/circuits/circuit_create.html +1 -7
  21. nautobot/circuits/views.py +3 -3
  22. nautobot/cloud/api/views.py +6 -10
  23. nautobot/cloud/models.py +1 -1
  24. nautobot/cloud/views.py +0 -16
  25. nautobot/core/api/constants.py +11 -0
  26. nautobot/core/api/fields.py +5 -5
  27. nautobot/core/api/filter_backends.py +3 -9
  28. nautobot/core/api/schema.py +13 -2
  29. nautobot/core/api/serializers.py +40 -34
  30. nautobot/core/api/views.py +56 -4
  31. nautobot/core/celery/log.py +4 -4
  32. nautobot/core/celery/schedulers.py +2 -2
  33. nautobot/core/choices.py +2 -2
  34. nautobot/core/events/__init__.py +3 -3
  35. nautobot/core/filters.py +67 -35
  36. nautobot/core/forms/__init__.py +19 -19
  37. nautobot/core/forms/fields.py +14 -11
  38. nautobot/core/forms/forms.py +33 -2
  39. nautobot/core/graphql/types.py +1 -1
  40. nautobot/core/jobs/__init__.py +28 -7
  41. nautobot/core/jobs/bulk_actions.py +285 -0
  42. nautobot/core/jobs/cleanup.py +48 -12
  43. nautobot/core/jobs/groups.py +1 -1
  44. nautobot/core/management/commands/validate_models.py +1 -1
  45. nautobot/core/models/__init__.py +3 -1
  46. nautobot/core/models/query_functions.py +2 -2
  47. nautobot/core/models/tree_queries.py +6 -3
  48. nautobot/core/settings.py +29 -2
  49. nautobot/core/settings.yaml +21 -0
  50. nautobot/core/tables.py +79 -61
  51. nautobot/core/templates/about.html +67 -0
  52. nautobot/core/templates/inc/media.html +3 -0
  53. nautobot/core/templates/inc/nav_menu.html +1 -0
  54. nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
  55. nautobot/core/templates/inc/tenant_table_row.html +11 -0
  56. nautobot/core/templates/nautobot_config.py.j2 +13 -0
  57. nautobot/core/templates/search.html +7 -0
  58. nautobot/core/templates/utilities/render_jinja2.html +1 -1
  59. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  60. nautobot/core/templates/utilities/theme_preview.html +7 -0
  61. nautobot/core/templatetags/helpers.py +11 -2
  62. nautobot/core/testing/__init__.py +8 -8
  63. nautobot/core/testing/api.py +170 -15
  64. nautobot/core/testing/filters.py +45 -10
  65. nautobot/core/testing/forms.py +2 -0
  66. nautobot/core/testing/integration.py +86 -4
  67. nautobot/core/testing/mixins.py +7 -2
  68. nautobot/core/testing/views.py +44 -29
  69. nautobot/core/tests/integration/test_app_home.py +0 -1
  70. nautobot/core/tests/integration/test_app_navbar.py +0 -1
  71. nautobot/core/tests/integration/test_filters.py +0 -2
  72. nautobot/core/tests/integration/test_home.py +0 -1
  73. nautobot/core/tests/integration/test_navbar.py +0 -1
  74. nautobot/core/tests/integration/test_view_authentication.py +1 -0
  75. nautobot/core/tests/runner.py +1 -1
  76. nautobot/core/tests/test_api.py +98 -1
  77. nautobot/core/tests/test_csv.py +25 -3
  78. nautobot/core/tests/test_filters.py +209 -246
  79. nautobot/core/tests/test_forms.py +1 -0
  80. nautobot/core/tests/test_jobs.py +460 -1
  81. nautobot/core/tests/test_models.py +9 -0
  82. nautobot/core/tests/test_settings_schema.py +7 -0
  83. nautobot/core/tests/test_tables.py +100 -0
  84. nautobot/core/tests/test_utils.py +63 -1
  85. nautobot/core/tests/test_views.py +30 -3
  86. nautobot/core/ui/nav.py +1 -0
  87. nautobot/core/ui/object_detail.py +15 -1
  88. nautobot/core/urls.py +11 -0
  89. nautobot/core/utils/lookup.py +11 -8
  90. nautobot/core/utils/querysets.py +64 -0
  91. nautobot/core/utils/requests.py +24 -9
  92. nautobot/core/views/__init__.py +42 -0
  93. nautobot/core/views/generic.py +131 -197
  94. nautobot/core/views/mixins.py +126 -38
  95. nautobot/core/views/renderers.py +6 -6
  96. nautobot/dcim/api/serializers.py +56 -64
  97. nautobot/dcim/api/views.py +47 -113
  98. nautobot/dcim/constants.py +6 -13
  99. nautobot/dcim/factory.py +6 -1
  100. nautobot/dcim/filters/__init__.py +31 -2
  101. nautobot/dcim/forms.py +36 -17
  102. nautobot/dcim/graphql/types.py +2 -2
  103. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
  104. nautobot/dcim/models/__init__.py +1 -1
  105. nautobot/dcim/models/device_component_templates.py +2 -2
  106. nautobot/dcim/models/device_components.py +22 -20
  107. nautobot/dcim/models/devices.py +10 -1
  108. nautobot/dcim/models/locations.py +3 -3
  109. nautobot/dcim/models/power.py +6 -5
  110. nautobot/dcim/models/racks.py +4 -4
  111. nautobot/dcim/tables/__init__.py +3 -3
  112. nautobot/dcim/tables/devices.py +7 -5
  113. nautobot/dcim/tables/devicetypes.py +2 -2
  114. nautobot/dcim/tables/racks.py +1 -1
  115. nautobot/dcim/templates/dcim/controller_create.html +1 -7
  116. nautobot/dcim/templates/dcim/controller_retrieve.html +1 -9
  117. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +2 -0
  118. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +5 -0
  119. nautobot/dcim/templates/dcim/device.html +1 -9
  120. nautobot/dcim/templates/dcim/device_edit.html +36 -37
  121. nautobot/dcim/templates/dcim/location.html +1 -9
  122. nautobot/dcim/templates/dcim/location_edit.html +1 -7
  123. nautobot/dcim/templates/dcim/rack.html +1 -9
  124. nautobot/dcim/templates/dcim/rack_edit.html +1 -7
  125. nautobot/dcim/templates/dcim/rackreservation.html +1 -9
  126. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -9
  127. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +1 -7
  128. nautobot/dcim/tests/integration/test_controller.py +62 -0
  129. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
  130. nautobot/dcim/tests/integration/test_device_bulk_delete.py +189 -0
  131. nautobot/dcim/tests/integration/test_device_bulk_edit.py +181 -0
  132. nautobot/dcim/tests/test_api.py +16 -5
  133. nautobot/dcim/tests/test_filters.py +33 -0
  134. nautobot/dcim/tests/test_forms.py +51 -2
  135. nautobot/dcim/tests/test_graphql.py +52 -0
  136. nautobot/dcim/tests/test_jobs.py +118 -0
  137. nautobot/dcim/tests/test_models.py +52 -9
  138. nautobot/dcim/tests/test_views.py +21 -83
  139. nautobot/dcim/views.py +1 -13
  140. nautobot/extras/api/customfields.py +2 -2
  141. nautobot/extras/api/serializers.py +90 -85
  142. nautobot/extras/api/views.py +22 -27
  143. nautobot/extras/constants.py +2 -0
  144. nautobot/extras/filters/__init__.py +8 -6
  145. nautobot/extras/forms/base.py +2 -2
  146. nautobot/extras/forms/forms.py +139 -31
  147. nautobot/extras/forms/mixins.py +14 -6
  148. nautobot/extras/group_sync.py +3 -3
  149. nautobot/extras/health_checks.py +1 -2
  150. nautobot/extras/jobs.py +85 -18
  151. nautobot/extras/managers.py +3 -1
  152. nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
  153. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
  154. nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
  155. nautobot/extras/models/__init__.py +1 -1
  156. nautobot/extras/models/contacts.py +1 -1
  157. nautobot/extras/models/customfields.py +12 -11
  158. nautobot/extras/models/groups.py +11 -9
  159. nautobot/extras/models/jobs.py +23 -4
  160. nautobot/extras/models/models.py +2 -2
  161. nautobot/extras/plugins/__init__.py +13 -2
  162. nautobot/extras/plugins/marketplace_manifest.yml +84 -79
  163. nautobot/extras/plugins/tables.py +16 -14
  164. nautobot/extras/plugins/views.py +65 -69
  165. nautobot/extras/registry.py +1 -1
  166. nautobot/extras/secrets/__init__.py +2 -2
  167. nautobot/extras/tables.py +7 -5
  168. nautobot/extras/templates/extras/dynamicgroup.html +1 -9
  169. nautobot/extras/templates/extras/job_detail.html +16 -0
  170. nautobot/extras/templates/extras/job_edit.html +1 -0
  171. nautobot/extras/templates/extras/jobqueue_retrieve.html +1 -9
  172. nautobot/extras/templates/extras/marketplace.html +29 -11
  173. nautobot/extras/templates/extras/plugin_detail.html +32 -15
  174. nautobot/extras/templates/extras/plugins_tiles.html +21 -10
  175. nautobot/extras/templatetags/job_buttons.py +4 -4
  176. nautobot/extras/test_jobs/api_test_job.py +1 -1
  177. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  178. nautobot/extras/test_jobs/dry_run.py +1 -1
  179. nautobot/extras/test_jobs/fail.py +5 -5
  180. nautobot/extras/test_jobs/file_output.py +1 -1
  181. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  182. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  183. nautobot/extras/test_jobs/ipaddress_vars.py +3 -1
  184. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  185. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  186. nautobot/extras/test_jobs/log_redaction.py +1 -1
  187. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  188. nautobot/extras/test_jobs/modify_db.py +1 -1
  189. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  190. nautobot/extras/test_jobs/object_var_required.py +1 -1
  191. nautobot/extras/test_jobs/object_vars.py +1 -1
  192. nautobot/extras/test_jobs/pass.py +3 -3
  193. nautobot/extras/test_jobs/profiling.py +1 -1
  194. nautobot/extras/test_jobs/relative_import.py +3 -3
  195. nautobot/extras/test_jobs/singleton.py +16 -0
  196. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  197. nautobot/extras/test_jobs/task_queues.py +1 -1
  198. nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
  199. nautobot/extras/tests/test_api.py +13 -13
  200. nautobot/extras/tests/test_customfields.py +1 -1
  201. nautobot/extras/tests/test_datasources.py +2 -1
  202. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  203. nautobot/extras/tests/test_filters.py +6 -6
  204. nautobot/extras/tests/test_forms.py +33 -1
  205. nautobot/extras/tests/test_jobs.py +178 -32
  206. nautobot/extras/tests/test_models.py +16 -10
  207. nautobot/extras/tests/test_plugins.py +62 -9
  208. nautobot/extras/tests/test_relationships.py +120 -9
  209. nautobot/extras/tests/test_views.py +56 -194
  210. nautobot/extras/utils.py +3 -2
  211. nautobot/extras/views.py +30 -98
  212. nautobot/ipam/api/fields.py +3 -3
  213. nautobot/ipam/api/serializers.py +41 -33
  214. nautobot/ipam/api/views.py +68 -117
  215. nautobot/ipam/factory.py +1 -1
  216. nautobot/ipam/filters.py +3 -2
  217. nautobot/ipam/lookups.py +101 -62
  218. nautobot/ipam/models.py +66 -16
  219. nautobot/ipam/querysets.py +2 -2
  220. nautobot/ipam/tables.py +23 -7
  221. nautobot/ipam/templates/ipam/ipaddress.html +1 -9
  222. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
  223. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
  224. nautobot/ipam/templates/ipam/prefix.html +1 -9
  225. nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
  226. nautobot/ipam/templates/ipam/vlan.html +1 -9
  227. nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
  228. nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
  229. nautobot/ipam/tests/test_api.py +436 -3
  230. nautobot/ipam/tests/test_forms.py +49 -47
  231. nautobot/ipam/tests/test_migrations.py +30 -30
  232. nautobot/ipam/tests/test_models.py +95 -34
  233. nautobot/ipam/tests/test_querysets.py +63 -1
  234. nautobot/ipam/tests/test_views.py +3 -0
  235. nautobot/ipam/utils/__init__.py +36 -6
  236. nautobot/ipam/views.py +61 -87
  237. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  238. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  239. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
  240. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  241. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  242. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  243. nautobot/project-static/docs/404.html +46 -4
  244. nautobot/project-static/docs/apps/index.html +46 -4
  245. nautobot/project-static/docs/apps/nautobot-apps.html +47 -6
  246. nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
  247. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.88dd0f4e.min.js} +2 -2
  248. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.88dd0f4e.min.js.map} +2 -2
  249. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +62 -10
  250. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +59 -7
  251. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +374 -122
  252. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +90 -18
  253. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +95 -21
  254. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +53 -6
  255. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +52 -5
  256. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +79 -17
  257. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +102 -28
  258. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +108 -21
  259. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +131 -38
  260. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +239 -65
  261. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +581 -165
  262. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +109 -36
  263. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +453 -167
  264. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +493 -211
  265. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +60 -8
  266. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +71 -15
  267. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +407 -55
  268. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +620 -205
  269. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +858 -412
  270. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +59 -7
  271. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +448 -186
  272. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +365 -147
  273. nautobot/project-static/docs/development/apps/api/configuration-view.html +46 -4
  274. nautobot/project-static/docs/development/apps/api/database-backend-config.html +46 -4
  275. nautobot/project-static/docs/development/apps/api/models/django-admin.html +46 -4
  276. nautobot/project-static/docs/development/apps/api/models/global-search.html +46 -4
  277. nautobot/project-static/docs/development/apps/api/models/graphql.html +46 -4
  278. nautobot/project-static/docs/development/apps/api/models/index.html +46 -4
  279. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +46 -4
  280. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +46 -4
  281. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +46 -4
  282. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +46 -4
  283. nautobot/project-static/docs/development/apps/api/platform-features/index.html +46 -4
  284. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +46 -4
  285. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +46 -4
  286. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +46 -4
  287. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +46 -4
  288. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +68 -7
  289. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +46 -4
  290. nautobot/project-static/docs/development/apps/api/prometheus.html +46 -4
  291. nautobot/project-static/docs/development/apps/api/setup.html +46 -4
  292. nautobot/project-static/docs/development/apps/api/testing.html +46 -4
  293. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +46 -4
  294. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +46 -4
  295. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +46 -4
  296. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +46 -4
  297. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +46 -4
  298. nautobot/project-static/docs/development/apps/api/views/base-template.html +46 -4
  299. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +46 -4
  300. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +46 -4
  301. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +46 -4
  302. nautobot/project-static/docs/development/apps/api/views/index.html +46 -4
  303. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +46 -4
  304. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +46 -4
  305. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +46 -4
  306. nautobot/project-static/docs/development/apps/api/views/notes.html +46 -4
  307. nautobot/project-static/docs/development/apps/api/views/rest-api.html +52 -6
  308. nautobot/project-static/docs/development/apps/api/views/urls.html +46 -4
  309. nautobot/project-static/docs/development/apps/index.html +46 -4
  310. nautobot/project-static/docs/development/apps/migration/code-updates.html +46 -4
  311. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +46 -4
  312. nautobot/project-static/docs/development/apps/migration/from-v1.html +46 -4
  313. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +46 -4
  314. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +46 -4
  315. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +46 -4
  316. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +46 -4
  317. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +50 -8
  318. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +46 -4
  319. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +211 -14
  320. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +46 -4
  321. nautobot/project-static/docs/development/apps/porting-from-netbox.html +46 -4
  322. nautobot/project-static/docs/development/core/application-registry.html +46 -4
  323. nautobot/project-static/docs/development/core/best-practices.html +46 -4
  324. nautobot/project-static/docs/development/core/bootstrap-ui.html +46 -4
  325. nautobot/project-static/docs/development/core/caching.html +46 -4
  326. nautobot/project-static/docs/development/core/controllers.html +46 -4
  327. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +73 -74
  328. nautobot/project-static/docs/development/core/generic-views.html +46 -4
  329. nautobot/project-static/docs/development/core/getting-started.html +249 -224
  330. nautobot/project-static/docs/development/core/homepage.html +49 -7
  331. nautobot/project-static/docs/development/core/index.html +46 -4
  332. nautobot/project-static/docs/development/core/{local-k8s.html → minikube-dev-environment-for-k8s-jobs.html} +469 -168
  333. nautobot/project-static/docs/development/core/model-checklist.html +56 -12
  334. nautobot/project-static/docs/development/core/model-features.html +46 -4
  335. nautobot/project-static/docs/development/core/natural-keys.html +46 -4
  336. nautobot/project-static/docs/development/core/navigation-menu.html +46 -4
  337. nautobot/project-static/docs/development/core/release-checklist.html +49 -7
  338. nautobot/project-static/docs/development/core/role-internals.html +46 -4
  339. nautobot/project-static/docs/development/core/settings.html +46 -4
  340. nautobot/project-static/docs/development/core/style-guide.html +49 -7
  341. nautobot/project-static/docs/development/core/templates.html +46 -4
  342. nautobot/project-static/docs/development/core/testing.html +46 -4
  343. nautobot/project-static/docs/development/core/ui-component-framework.html +369 -273
  344. nautobot/project-static/docs/development/core/user-preferences.html +46 -4
  345. nautobot/project-static/docs/development/index.html +46 -4
  346. nautobot/project-static/docs/development/jobs/index.html +216 -122
  347. nautobot/project-static/docs/development/jobs/migration/from-v1.html +46 -4
  348. nautobot/project-static/docs/index.html +54 -23
  349. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  350. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  351. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  352. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  353. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  354. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  355. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  356. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  357. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  358. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  359. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  360. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  361. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  362. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  363. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  364. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  365. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  366. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  367. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  368. nautobot/project-static/docs/objects.inv +0 -0
  369. nautobot/project-static/docs/overview/application_stack.html +47 -7
  370. nautobot/project-static/docs/overview/design_philosophy.html +46 -4
  371. nautobot/project-static/docs/release-notes/index.html +52 -12
  372. nautobot/project-static/docs/release-notes/version-1.0.html +234 -193
  373. nautobot/project-static/docs/release-notes/version-1.1.html +231 -190
  374. nautobot/project-static/docs/release-notes/version-1.2.html +306 -265
  375. nautobot/project-static/docs/release-notes/version-1.3.html +332 -291
  376. nautobot/project-static/docs/release-notes/version-1.4.html +417 -377
  377. nautobot/project-static/docs/release-notes/version-1.5.html +605 -566
  378. nautobot/project-static/docs/release-notes/version-1.6.html +904 -447
  379. nautobot/project-static/docs/release-notes/version-2.0.html +528 -489
  380. nautobot/project-static/docs/release-notes/version-2.1.html +363 -324
  381. nautobot/project-static/docs/release-notes/version-2.2.html +356 -317
  382. nautobot/project-static/docs/release-notes/version-2.3.html +997 -352
  383. nautobot/project-static/docs/release-notes/version-2.4.html +525 -101
  384. nautobot/project-static/docs/requirements.txt +2 -2
  385. nautobot/project-static/docs/search/search_index.json +1 -1
  386. nautobot/project-static/docs/sitemap.xml +295 -287
  387. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  388. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +46 -4
  389. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +46 -4
  390. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +48 -6
  391. nautobot/project-static/docs/user-guide/administration/configuration/index.html +46 -4
  392. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +46 -4
  393. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +110 -8
  394. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +46 -4
  395. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +46 -4
  396. nautobot/project-static/docs/user-guide/administration/guides/docker.html +46 -4
  397. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +46 -4
  398. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +46 -4
  399. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +46 -4
  400. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +46 -4
  401. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +46 -4
  402. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +48 -6
  403. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +46 -4
  404. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +46 -4
  405. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +46 -4
  406. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +66 -8
  407. nautobot/project-static/docs/user-guide/administration/installation/index.html +46 -4
  408. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +47 -5
  409. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +46 -4
  410. nautobot/project-static/docs/user-guide/administration/installation/services.html +46 -4
  411. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +46 -4
  412. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +46 -4
  413. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +46 -4
  414. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +46 -4
  415. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +46 -4
  416. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +46 -4
  417. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +46 -4
  418. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +46 -4
  419. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +46 -4
  420. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +49 -8
  421. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +46 -4
  422. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +46 -4
  423. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +46 -4
  424. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +46 -4
  425. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +46 -4
  426. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +46 -4
  427. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +46 -4
  428. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +46 -4
  429. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +46 -4
  430. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +46 -4
  431. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +46 -4
  432. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +46 -4
  433. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +46 -4
  434. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +46 -4
  435. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +46 -4
  436. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +46 -4
  437. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +46 -4
  438. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +46 -4
  439. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +46 -4
  440. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +46 -4
  441. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +46 -4
  442. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +46 -4
  443. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +46 -4
  444. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +46 -4
  445. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +46 -4
  446. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +46 -4
  447. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +46 -4
  448. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +46 -4
  449. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +46 -4
  450. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +46 -4
  451. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +46 -4
  452. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +46 -4
  453. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +46 -4
  454. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +46 -4
  455. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +46 -4
  456. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +46 -4
  457. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +46 -4
  458. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +46 -4
  459. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +46 -4
  460. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +46 -4
  461. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +46 -4
  462. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +46 -4
  463. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +46 -4
  464. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +46 -4
  465. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +46 -4
  466. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +46 -4
  467. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +46 -4
  468. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +46 -4
  469. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +46 -4
  470. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +46 -4
  471. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +46 -4
  472. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +46 -4
  473. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +46 -4
  474. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +46 -4
  475. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +46 -4
  476. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +46 -4
  477. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +46 -4
  478. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +50 -12
  479. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +46 -4
  480. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +46 -4
  481. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +46 -4
  482. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +46 -4
  483. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +46 -4
  484. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +46 -4
  485. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +46 -4
  486. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +46 -4
  487. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +46 -4
  488. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +46 -4
  489. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +46 -4
  490. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +46 -4
  491. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +49 -7
  492. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +46 -4
  493. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +46 -4
  494. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +46 -4
  495. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +46 -4
  496. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +46 -4
  497. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +46 -4
  498. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +46 -4
  499. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +46 -4
  500. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +46 -4
  501. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +46 -4
  502. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +46 -4
  503. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +46 -4
  504. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +46 -4
  505. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +46 -4
  506. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +46 -4
  507. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +46 -4
  508. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +46 -4
  509. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +46 -4
  510. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +46 -4
  511. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +46 -4
  512. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +46 -4
  513. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +46 -4
  514. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +51 -7
  515. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +46 -4
  516. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  517. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  518. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  519. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  520. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  521. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  522. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  523. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  524. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  525. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  526. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  527. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  528. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  529. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +46 -4
  530. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +46 -4
  531. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +49 -7
  532. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
  533. nautobot/project-static/docs/user-guide/index.html +46 -4
  534. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +46 -4
  535. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +50 -8
  536. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +46 -4
  537. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +46 -4
  538. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +46 -4
  539. nautobot/project-static/docs/user-guide/platform-functionality/events.html +46 -4
  540. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +46 -4
  541. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +46 -4
  542. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +46 -4
  543. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +46 -4
  544. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +46 -4
  545. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +46 -4
  546. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +50 -7
  547. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +46 -4
  548. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +49 -7
  549. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +46 -4
  550. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +46 -4
  551. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
  552. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +46 -4
  553. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +46 -4
  554. nautobot/project-static/docs/user-guide/platform-functionality/note.html +46 -4
  555. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +46 -4
  556. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +47 -5
  557. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +46 -4
  558. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +46 -4
  559. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +94 -25
  560. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +74 -5
  561. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +46 -4
  562. nautobot/project-static/docs/user-guide/platform-functionality/role.html +46 -4
  563. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +46 -4
  564. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +46 -4
  565. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +46 -4
  566. nautobot/project-static/docs/user-guide/platform-functionality/status.html +46 -4
  567. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +46 -4
  568. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +46 -4
  569. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +46 -4
  570. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +46 -4
  571. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +46 -4
  572. nautobot/project-static/js/forms.js +1 -1
  573. nautobot/tenancy/api/views.py +9 -13
  574. nautobot/tenancy/views.py +4 -2
  575. nautobot/users/admin.py +1 -1
  576. nautobot/users/api/serializers.py +5 -4
  577. nautobot/users/api/views.py +3 -3
  578. nautobot/virtualization/api/serializers.py +4 -4
  579. nautobot/virtualization/api/views.py +5 -24
  580. nautobot/virtualization/filters.py +20 -3
  581. nautobot/virtualization/models.py +1 -1
  582. nautobot/virtualization/tables.py +2 -2
  583. nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
  584. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
  585. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
  586. nautobot/virtualization/tests/test_filters.py +17 -0
  587. nautobot/wireless/filters.py +2 -2
  588. nautobot/wireless/forms.py +1 -1
  589. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +1 -9
  590. nautobot/wireless/tests/integration/__init__.py +0 -0
  591. nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
  592. nautobot/wireless/tests/test_filters.py +29 -1
  593. nautobot/wireless/tests/test_views.py +22 -1
  594. nautobot/wireless/views.py +0 -10
  595. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/METADATA +6 -6
  596. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/RECORD +600 -550
  597. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/WHEEL +1 -1
  598. nautobot/core/fixtures/user-data.json +0 -59
  599. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/LICENSE.txt +0 -0
  600. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/NOTICE +0 -0
  601. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/entry_points.txt +0 -0
@@ -12,7 +12,7 @@ from nautobot.core.forms import (
12
12
  DynamicModelMultipleChoiceField,
13
13
  )
14
14
  from nautobot.core.tables import RelationshipColumn
15
- from nautobot.core.testing import TestCase
15
+ from nautobot.core.testing import create_job_result_and_run_job, TestCase, TransactionTestCase
16
16
  from nautobot.core.testing.models import ModelTestCases
17
17
  from nautobot.core.utils.lookup import get_route_for_model
18
18
  from nautobot.dcim.forms import DeviceForm
@@ -29,9 +29,22 @@ from nautobot.dcim.models import (
29
29
  )
30
30
  from nautobot.dcim.tables import LocationTable
31
31
  from nautobot.dcim.tests.test_views import create_test_device
32
- from nautobot.extras.choices import RelationshipRequiredSideChoices, RelationshipSideChoices, RelationshipTypeChoices
33
- from nautobot.extras.models import Relationship, RelationshipAssociation, Role, Status
34
- from nautobot.ipam.models import VLAN, VLANGroup
32
+ from nautobot.extras.choices import (
33
+ JobResultStatusChoices,
34
+ LogLevelChoices,
35
+ RelationshipRequiredSideChoices,
36
+ RelationshipSideChoices,
37
+ RelationshipTypeChoices,
38
+ )
39
+ from nautobot.extras.models import (
40
+ Relationship,
41
+ RelationshipAssociation,
42
+ Role,
43
+ Status,
44
+ )
45
+ from nautobot.extras.models.jobs import JobLogEntry
46
+ from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
47
+ from nautobot.wireless.models import ControllerManagedDeviceGroupWirelessNetworkAssignment
35
48
 
36
49
 
37
50
  class RelationshipBaseTest:
@@ -354,20 +367,24 @@ class RelationshipTest(RelationshipBaseTest, ModelTestCases.BaseModelTestCase):
354
367
  self.assertFalse(field.required)
355
368
  self.assertIsInstance(field, DynamicModelMultipleChoiceField)
356
369
  self.assertEqual(field.label, "My VLANs")
357
- self.assertEqual(field.query_params, {})
370
+ self.assertEqual(field.query_params, {"exclude_m2m": "true"})
358
371
 
359
372
  field = self.m2m_1.to_form_field("destination")
360
373
  self.assertFalse(field.required)
361
374
  self.assertIsInstance(field, DynamicModelMultipleChoiceField)
362
375
  self.assertEqual(field.label, "My Racks")
363
376
  self.assertEqual(
364
- field.query_params, {"location": [self.locations[0].name, self.locations[1].name, self.locations[2].name]}
377
+ field.query_params,
378
+ {
379
+ "location": [self.locations[0].name, self.locations[1].name, self.locations[2].name],
380
+ "exclude_m2m": "true",
381
+ },
365
382
  )
366
383
 
367
384
  field = self.m2ms_1.to_form_field("peer")
368
385
  self.assertFalse(field.required)
369
386
  self.assertIsInstance(field, DynamicModelMultipleChoiceField)
370
- self.assertEqual(field.query_params, {})
387
+ self.assertEqual(field.query_params, {"exclude_m2m": "true"})
371
388
 
372
389
  def test_to_form_field_o2m(self):
373
390
  field = self.o2m_1.to_form_field("source")
@@ -1326,7 +1343,7 @@ class RelationshipTableTest(RelationshipBaseTest, TestCase):
1326
1343
  self.assertIsNotNone(relationship_column)
1327
1344
  self.assertIsInstance(relationship_column, RelationshipColumn)
1328
1345
 
1329
- rendered_value = bound_row.get_cell(internal_col_name)
1346
+ rendered_value = bound_row.get_cell(internal_col_name) # pylint: disable=no-member
1330
1347
  # Test if the expected value is in the rendered value.
1331
1348
  # Exact match is difficult because the order of rendering is unpredictable.
1332
1349
  for value in col_expected_value:
@@ -1432,7 +1449,7 @@ class RequiredRelationshipTestMixin:
1432
1449
  required_on="source",
1433
1450
  )
1434
1451
  relationship_o2o.validated_save()
1435
- vlan_group = VLANGroup.objects.first()
1452
+ vlan_group = VLANGroup.objects.create(name="Test VLANGroup 1")
1436
1453
 
1437
1454
  tests_params = [
1438
1455
  # Required many-to-many:
@@ -1712,3 +1729,97 @@ class RequiredRelationshipTestMixin:
1712
1729
  }
1713
1730
  }
1714
1731
  self.assertEqual(expected_error_json, response.json())
1732
+
1733
+
1734
+ class RelationshipJobTestCase(RequiredRelationshipTestMixin, TransactionTestCase):
1735
+ databases = ("default", "job_logs")
1736
+
1737
+ def create_job(self, pk_list, **extra_form_data):
1738
+ """"""
1739
+ vlan_ct = ContentType.objects.get_for_model(VLAN)
1740
+ job_result = create_job_result_and_run_job(
1741
+ "nautobot.core.jobs.bulk_actions",
1742
+ "BulkEditObjects",
1743
+ content_type=vlan_ct.id,
1744
+ edit_all=False,
1745
+ filter_query_params={},
1746
+ form_data={"pk": pk_list, **extra_form_data},
1747
+ username=self.user.username,
1748
+ )
1749
+ return job_result
1750
+
1751
+ def test_required_relationships(self):
1752
+ """
1753
+ 1. Try creating an object when no required target object exists
1754
+ 2. Try creating an object without specifying required target object(s)
1755
+ 3. Try creating an object when all required data is present
1756
+ 4. Test bulk edit
1757
+ """
1758
+
1759
+ # Delete existing factory generated objects that may interfere with this test
1760
+ IPAddress.objects.all().delete()
1761
+ Prefix.objects.update(parent=None)
1762
+ Prefix.objects.all().delete()
1763
+ ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.all().delete()
1764
+ VLAN.objects.all().delete()
1765
+
1766
+ # Parameterized tests (for creating and updating single objects):
1767
+ self.required_relationships_test(interact_with="ui")
1768
+
1769
+ # 4. Bulk create/edit tests:
1770
+
1771
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1772
+ vlans = (
1773
+ VLAN.objects.create(name="test_required_relationships1", vid=1, status=vlan_status),
1774
+ VLAN.objects.create(name="test_required_relationships2", vid=2, status=vlan_status),
1775
+ VLAN.objects.create(name="test_required_relationships3", vid=3, status=vlan_status),
1776
+ VLAN.objects.create(name="test_required_relationships4", vid=4, status=vlan_status),
1777
+ VLAN.objects.create(name="test_required_relationships5", vid=5, status=vlan_status),
1778
+ VLAN.objects.create(name="test_required_relationships6", vid=6, status=vlan_status),
1779
+ )
1780
+
1781
+ # Try deleting all devices and then editing the 6 VLANs (fails):
1782
+ Controller.objects.filter(controller_device__isnull=False).delete()
1783
+ Device.objects.all().delete()
1784
+
1785
+ pk_list = [str(vlan.id) for vlan in vlans]
1786
+ job_result = self.create_job(pk_list)
1787
+ self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
1788
+ error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
1789
+ self.assertIn("VLANs require at least one device, but no devices exist yet.", error_log.message)
1790
+
1791
+ # Create test device for association
1792
+ device_for_association = create_test_device("VLAN Required Device")
1793
+
1794
+ # Try editing all 6 VLANs without adding the required device(fails):
1795
+ job_result = self.create_job(pk_list)
1796
+ self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
1797
+ error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
1798
+ self.assertIn(
1799
+ '6 VLANs require a device for the required relationship \\"VLANs require at least one Device',
1800
+ error_log.message,
1801
+ )
1802
+
1803
+ # Try editing 3 VLANs without adding the required device(fails):
1804
+ job_result = self.create_job(pk_list[:3])
1805
+ self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
1806
+ error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
1807
+ self.assertIn(
1808
+ 'These VLANs require a device for the required relationship \\"VLANs require at least one Device',
1809
+ error_log.message,
1810
+ )
1811
+ for vlan in vlans[:3]:
1812
+ self.assertIn(str(vlan), error_log.message)
1813
+
1814
+ # Try editing 6 VLANs and adding the required device (succeeds):
1815
+ job_result = self.create_job(pk_list, add_cr_vlans_devices_m2m__source=[str(device_for_association.id)])
1816
+ self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
1817
+
1818
+ # Try editing 6 VLANs and removing the required device (fails):
1819
+ job_result = self.create_job(pk_list, remove_cr_vlans_devices_m2m__source=[str(device_for_association.id)])
1820
+ self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
1821
+ error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
1822
+ self.assertIn(
1823
+ '6 VLANs require a device for the required relationship \\"VLANs require at least one Device',
1824
+ error_log.message,
1825
+ )
@@ -1,5 +1,4 @@
1
1
  from datetime import timedelta
2
- from http import HTTPStatus
3
2
  import json
4
3
  from unittest import mock
5
4
  import urllib.parse
@@ -26,7 +25,6 @@ from nautobot.core.testing.utils import disable_warnings, get_deletable_objects,
26
25
  from nautobot.core.utils.permissions import get_permission_for_model
27
26
  from nautobot.dcim.models import (
28
27
  ConsolePort,
29
- Controller,
30
28
  Device,
31
29
  DeviceType,
32
30
  Interface,
@@ -34,7 +32,6 @@ from nautobot.dcim.models import (
34
32
  LocationType,
35
33
  Manufacturer,
36
34
  )
37
- from nautobot.dcim.tests import test_views
38
35
  from nautobot.extras.choices import (
39
36
  CustomFieldTypeChoices,
40
37
  DynamicGroupTypeChoices,
@@ -89,10 +86,9 @@ from nautobot.extras.templatetags.job_buttons import NO_CONFIRM_BUTTON
89
86
  from nautobot.extras.tests.constants import BIG_GRAPHQL_DEVICE_QUERY
90
87
  from nautobot.extras.tests.test_relationships import RequiredRelationshipTestMixin
91
88
  from nautobot.extras.utils import RoleModelsQuery, TaggableClassesQuery
92
- from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
89
+ from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup, VRF
93
90
  from nautobot.tenancy.models import Tenant
94
91
  from nautobot.users.models import ObjectPermission
95
- from nautobot.wireless.models import ControllerManagedDeviceGroupWirelessNetworkAssignment
96
92
 
97
93
  # Use the proper swappable User model
98
94
  User = get_user_model()
@@ -915,6 +911,30 @@ class DynamicGroupTestCase(
915
911
  self.form_data["content_type"] = instance.content_type.pk # Content-type is not editable after creation
916
912
  super().test_edit_object_with_constrained_permission()
917
913
 
914
+ def test_edit_object_with_content_type_ipam_prefix(self):
915
+ """Assert bug fix #6526: `Error when defining Dynamic Group of Prefixes using `present_in_vrf_id` filter`"""
916
+ content_type = ContentType.objects.get_for_model(Prefix)
917
+ instance = DynamicGroup.objects.create(name="DG Ipam|Prefix", content_type=content_type)
918
+ vrf_instance = VRF.objects.first()
919
+ data = self.form_data.copy()
920
+ data.update(
921
+ {
922
+ "name": "DG Ipam|Prefix",
923
+ "content_type": content_type.pk,
924
+ "filter-present_in_vrf_id": vrf_instance.id,
925
+ "tenant": None,
926
+ "tags": [],
927
+ }
928
+ )
929
+ self.add_permissions("extras.change_dynamicgroup")
930
+ request = {
931
+ "path": self._get_url("edit", instance),
932
+ "data": post_data(data),
933
+ }
934
+ self.assertHttpStatus(self.client.post(**request), 302)
935
+ instance.refresh_from_db()
936
+ self.assertEqual(instance.filter["present_in_vrf_id"], str(vrf_instance.id))
937
+
918
938
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
919
939
  def test_edit_saved_filter(self):
920
940
  """Test that editing a filter works using the edit view."""
@@ -1698,21 +1718,21 @@ class ScheduledJobTestCase(
1698
1718
  user = User.objects.create(username="user1", is_active=True)
1699
1719
  ScheduledJob.objects.create(
1700
1720
  name="test1",
1701
- task="pass.TestPass",
1721
+ task="pass.TestPassJob",
1702
1722
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1703
1723
  user=user,
1704
1724
  start_time=timezone.now(),
1705
1725
  )
1706
1726
  ScheduledJob.objects.create(
1707
1727
  name="test2",
1708
- task="pass.TestPass",
1728
+ task="pass.TestPassJob",
1709
1729
  interval=JobExecutionType.TYPE_DAILY,
1710
1730
  user=user,
1711
1731
  start_time=timezone.now(),
1712
1732
  )
1713
1733
  ScheduledJob.objects.create(
1714
1734
  name="test3",
1715
- task="pass.TestPass",
1735
+ task="pass.TestPassJob",
1716
1736
  interval=JobExecutionType.TYPE_CUSTOM,
1717
1737
  user=user,
1718
1738
  start_time=timezone.now(),
@@ -1726,7 +1746,7 @@ class ScheduledJobTestCase(
1726
1746
  ScheduledJob.objects.create(
1727
1747
  enabled=False,
1728
1748
  name="test4",
1729
- task="pass.TestPass",
1749
+ task="pass.TestPassJob",
1730
1750
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1731
1751
  user=self.user,
1732
1752
  start_time=timezone.now(),
@@ -1743,7 +1763,7 @@ class ScheduledJobTestCase(
1743
1763
  ScheduledJob.objects.create(
1744
1764
  enabled=True,
1745
1765
  name=name,
1746
- task="pass.TestPass",
1766
+ task="pass.TestPassJob",
1747
1767
  interval=JobExecutionType.TYPE_CUSTOM,
1748
1768
  user=self.user,
1749
1769
  start_time=timezone.now(),
@@ -1774,7 +1794,7 @@ class ScheduledJobTestCase(
1774
1794
  ScheduledJob.objects.create(
1775
1795
  enabled=True,
1776
1796
  name="test11",
1777
- task="pass.TestPass",
1797
+ task="pass.TestPassJob",
1778
1798
  interval=JobExecutionType.TYPE_CUSTOM,
1779
1799
  user=self.user,
1780
1800
  start_time=timezone.now(),
@@ -1808,7 +1828,7 @@ class ApprovalQueueTestCase(
1808
1828
  def setUp(self):
1809
1829
  super().setUp()
1810
1830
  self.job_model = Job.objects.get_for_class_path("dry_run.TestDryRun")
1811
- self.job_model_2 = Job.objects.get_for_class_path("fail.TestFail")
1831
+ self.job_model_2 = Job.objects.get_for_class_path("fail.TestFailJob")
1812
1832
 
1813
1833
  ScheduledJob.objects.create(
1814
1834
  name="test1",
@@ -1821,7 +1841,7 @@ class ApprovalQueueTestCase(
1821
1841
  )
1822
1842
  ScheduledJob.objects.create(
1823
1843
  name="test2",
1824
- task="fail.TestFail",
1844
+ task="fail.TestFailJob",
1825
1845
  job_model=self.job_model_2,
1826
1846
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1827
1847
  user=self.user,
@@ -1834,7 +1854,7 @@ class ApprovalQueueTestCase(
1834
1854
 
1835
1855
  ScheduledJob.objects.create(
1836
1856
  name="test4",
1837
- task="pass.TestPass",
1857
+ task="pass.TestPassJob",
1838
1858
  job_model=self.job_model,
1839
1859
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1840
1860
  user=self.user,
@@ -2225,8 +2245,8 @@ class JobResultTestCase(
2225
2245
 
2226
2246
  @classmethod
2227
2247
  def setUpTestData(cls):
2228
- JobResult.objects.create(name="pass.TestPass")
2229
- JobResult.objects.create(name="fail.TestFail")
2248
+ JobResult.objects.create(name="pass.TestPassJob")
2249
+ JobResult.objects.create(name="fail.TestFailJob")
2230
2250
  JobLogEntry.objects.create(
2231
2251
  log_level=LogLevelChoices.LOG_INFO,
2232
2252
  job_result=JobResult.objects.first(),
@@ -2280,7 +2300,7 @@ class JobTestCase(
2280
2300
  # Job model objects are automatically created during database migrations
2281
2301
 
2282
2302
  # But we do need to make sure the ones we're testing are flagged appropriately
2283
- cls.test_pass = Job.objects.get(job_class_name="TestPass")
2303
+ cls.test_pass = Job.objects.get(job_class_name="TestPassJob")
2284
2304
  default_job_queue = JobQueue.objects.get(name="default", queue_type=JobQueueTypeChoices.TYPE_CELERY)
2285
2305
  cls.test_pass.default_job_queue = default_job_queue
2286
2306
  cls.test_pass.enabled = True
@@ -2416,35 +2436,6 @@ class JobTestCase(
2416
2436
  # assert Job still exists
2417
2437
  self.assertTrue(self._get_queryset().filter(name=job_name).exists())
2418
2438
 
2419
- def test_bulk_delete_system_jobs_fail(self):
2420
- system_job_queryset = self.model.objects.filter(module_name__startswith="nautobot.")
2421
- pk_list = system_job_queryset.values_list("pk", flat=True)[:3]
2422
- initial_count = self._get_queryset().count()
2423
- data = {
2424
- "pk": pk_list,
2425
- "confirm": True,
2426
- "_confirm": True, # Form button
2427
- }
2428
- # Try bulk delete with delete job permission
2429
- self.add_permissions("extras.delete_job")
2430
- response = self.client.post(self._get_url("bulk_delete"), data, follow=True)
2431
- self.assertBodyContains(
2432
- response,
2433
- f"Unable to delete Job {system_job_queryset.first()}. System Job cannot be deleted",
2434
- status_code=403,
2435
- )
2436
- self.assertEqual(self._get_queryset().count(), initial_count)
2437
-
2438
- # Try bulk delete as a superuser
2439
- self.user.is_superuser = True
2440
- response = self.client.post(self._get_url("bulk_delete"), data, follow=True)
2441
- self.assertBodyContains(
2442
- response,
2443
- f"Unable to delete Job {system_job_queryset.first()}. System Job cannot be deleted",
2444
- status_code=403,
2445
- )
2446
- self.assertEqual(self._get_queryset().count(), initial_count)
2447
-
2448
2439
  def validate_job_data_after_bulk_edit(self, pk_list, old_data):
2449
2440
  # Name is bulk-editable
2450
2441
  overridable_fields = [field for field in JOB_OVERRIDABLE_FIELDS if field != "name"]
@@ -2513,7 +2504,7 @@ class JobTestCase(
2513
2504
  self.add_permissions("extras.run_job")
2514
2505
  for run_url in self.run_urls:
2515
2506
  response = self.client.get(run_url)
2516
- self.assertBodyContains(response, "TestPass")
2507
+ self.assertBodyContains(response, "TestPassJob")
2517
2508
 
2518
2509
  @override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2519
2510
  def test_get_run_with_constrained_permission(self):
@@ -2612,12 +2603,12 @@ class JobTestCase(
2612
2603
  self.add_permissions("extras.run_job")
2613
2604
 
2614
2605
  for run_url in (
2615
- reverse("extras:job_run_by_class_path", kwargs={"class_path": "fail.TestFail"}),
2616
- reverse("extras:job_run", kwargs={"pk": Job.objects.get(job_class_name="TestFail").pk}),
2606
+ reverse("extras:job_run_by_class_path", kwargs={"class_path": "fail.TestFailJob"}),
2607
+ reverse("extras:job_run", kwargs={"pk": Job.objects.get(job_class_name="TestFailJob").pk}),
2617
2608
  ):
2618
2609
  response = self.client.post(run_url, self.data_run_immediately)
2619
2610
  self.assertBodyContains(response, "Job is not enabled to be run")
2620
- self.assertFalse(JobResult.objects.filter(name="fail.TestFail").exists())
2611
+ self.assertFalse(JobResult.objects.filter(name="fail.TestFailJob").exists())
2621
2612
 
2622
2613
  def test_run_now_missing_args(self):
2623
2614
  self.add_permissions("extras.run_job")
@@ -2653,10 +2644,16 @@ class JobTestCase(
2653
2644
  job_celery_kwargs = {
2654
2645
  "nautobot_job_job_model_id": self.test_required_args.id,
2655
2646
  "nautobot_job_profile": True,
2647
+ "nautobot_job_ignore_singleton_lock": True,
2656
2648
  "nautobot_job_user_id": self.user.id,
2657
2649
  "queue": job_queue.name,
2658
2650
  }
2659
2651
  self.test_required_args.job_queues.set([job_queue])
2652
+ self.test_required_args.is_singleton_override = True
2653
+ self.test_required_args.has_sensitive_variables_override = True
2654
+ self.test_required_args.is_singleton = True
2655
+ self.test_required_args.has_sensitive_variables = False
2656
+ self.test_required_args.validated_save()
2660
2657
  previous_result = JobResult.objects.create(
2661
2658
  job_model=self.test_required_args,
2662
2659
  user=self.user,
@@ -2673,6 +2670,9 @@ class JobTestCase(
2673
2670
  content,
2674
2671
  )
2675
2672
  self.assertInHTML('<input type="hidden" name="_profile" value="True" id="id__profile">', content)
2673
+ self.assertInHTML(
2674
+ '<input type="checkbox" name="_ignore_singleton_lock" id="id__ignore_singleton_lock" checked>', content
2675
+ )
2676
2676
 
2677
2677
  @mock.patch("nautobot.extras.views.get_worker_count", return_value=1)
2678
2678
  def test_run_later_missing_name(self, _):
@@ -2844,59 +2844,6 @@ class JobTestCase(
2844
2844
  response = self.client.get(instance.get_changelog_url())
2845
2845
  self.assertBodyContains(response, f"{instance.name} - Change Log")
2846
2846
 
2847
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2848
- def test_job_bulk_edit_default_queue_stays_default_after_one_field_update(self):
2849
- self.add_permissions("extras.change_job")
2850
- job_class_to_test = "TestPass"
2851
- job_description = "default job queue test"
2852
- job_to_test = self.model.objects.get(job_class_name=job_class_to_test)
2853
- queryset = self.model.objects.filter(installed=True, hidden=False, job_class_name=job_class_to_test)
2854
- default_job_queue = job_to_test.default_job_queue
2855
- pk_list = list(queryset.values_list("pk", flat=True))
2856
-
2857
- data = {
2858
- "description": job_description,
2859
- "pk": pk_list,
2860
- "_apply": True,
2861
- }
2862
-
2863
- self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), HTTPStatus.FOUND)
2864
- instance = self.model.objects.get(job_class_name=job_class_to_test)
2865
- self.assertEqual(instance.description, job_description)
2866
- self.assertEqual(instance.default_job_queue, default_job_queue)
2867
-
2868
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2869
- def test_job_bulk_edit_preserve_job_queues_after_one_field_update(self):
2870
- self.add_permissions("extras.change_job")
2871
- job_class_to_test = "TestPass"
2872
- job_description = "job queues test"
2873
- job_to_test = self.model.objects.get(job_class_name=job_class_to_test)
2874
-
2875
- # Simulate 3 job queues set
2876
- job_queues = JobQueue.objects.all()[:3]
2877
- job_to_test.job_queues.set(job_queues)
2878
- job_to_test.job_queues.add(job_to_test.default_job_queue)
2879
- job_to_test.job_queues_override = True
2880
- job_to_test.save()
2881
-
2882
- expected_job_queues = list(job_to_test.job_queues.values_list("pk", flat=True))
2883
-
2884
- queryset = self.model.objects.filter(installed=True, hidden=False, job_class_name=job_class_to_test)
2885
- pk_list = list(queryset.values_list("pk", flat=True))
2886
-
2887
- data = {
2888
- "description": job_description,
2889
- "pk": pk_list,
2890
- "_apply": True,
2891
- }
2892
-
2893
- self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), HTTPStatus.FOUND)
2894
-
2895
- instance = self.model.objects.get(job_class_name=job_class_to_test)
2896
- self.assertEqual(instance.description, job_description)
2897
- self.assertEqual(instance.job_queues.count(), 4) # 3 custom one + 1 default
2898
- self.assertQuerySetEqual(instance.job_queues.all(), JobQueue.objects.filter(pk__in=expected_job_queues))
2899
-
2900
2847
 
2901
2848
  class JobButtonTestCase(
2902
2849
  ViewTestCases.CreateObjectViewTestCase,
@@ -3008,8 +2955,9 @@ class JobButtonRenderingTestCase(TestCase):
3008
2955
  response = self.client.get(self.location_type.get_absolute_url(), follow=True)
3009
2956
  self.assertEqual(response.status_code, 200)
3010
2957
  content = extract_page_body(response.content.decode(response.charset))
3011
- job_queues = self.job.job_queues.all().values_list("name", flat=True)
3012
- self.assertIn(f'<input type="hidden" name="_job_queue" value="{job_queues[0]}">', content, content)
2958
+ job_queues = self.job.job_queues.all()
2959
+ _job_queue = job_queues[0]
2960
+ self.assertIn(f'<input type="hidden" name="_job_queue" value="{_job_queue.pk}">', content, content)
3013
2961
 
3014
2962
  self.job.job_queues_override = False
3015
2963
  self.job.save()
@@ -3018,7 +2966,7 @@ class JobButtonRenderingTestCase(TestCase):
3018
2966
  self.assertEqual(response.status_code, 200)
3019
2967
  content = extract_page_body(response.content.decode(response.charset))
3020
2968
  self.assertIn(
3021
- f'<input type="hidden" name="_job_queue" value="{self.job.default_job_queue.name}">', content, content
2969
+ f'<input type="hidden" name="_job_queue" value="{self.job.default_job_queue.pk}">', content, content
3022
2970
  )
3023
2971
 
3024
2972
  def test_view_object_with_unsafe_text(self):
@@ -3279,94 +3227,6 @@ class RelationshipTestCase(
3279
3227
 
3280
3228
  cls.slug_test_object = "Primary Interface"
3281
3229
 
3282
- def test_required_relationships(self):
3283
- """
3284
- 1. Try creating an object when no required target object exists
3285
- 2. Try creating an object without specifying required target object(s)
3286
- 3. Try creating an object when all required data is present
3287
- 4. Test bulk edit
3288
- """
3289
-
3290
- # Delete existing factory generated objects that may interfere with this test
3291
- IPAddress.objects.all().delete()
3292
- Prefix.objects.update(parent=None)
3293
- Prefix.objects.all().delete()
3294
- ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.all().delete()
3295
- VLAN.objects.all().delete()
3296
-
3297
- # Parameterized tests (for creating and updating single objects):
3298
- self.required_relationships_test(interact_with="ui")
3299
-
3300
- # 4. Bulk create/edit tests:
3301
-
3302
- vlan_status = Status.objects.get_for_model(VLAN).first()
3303
- vlans = (
3304
- VLAN.objects.create(name="test_required_relationships1", vid=1, status=vlan_status),
3305
- VLAN.objects.create(name="test_required_relationships2", vid=2, status=vlan_status),
3306
- VLAN.objects.create(name="test_required_relationships3", vid=3, status=vlan_status),
3307
- VLAN.objects.create(name="test_required_relationships4", vid=4, status=vlan_status),
3308
- VLAN.objects.create(name="test_required_relationships5", vid=5, status=vlan_status),
3309
- VLAN.objects.create(name="test_required_relationships6", vid=6, status=vlan_status),
3310
- )
3311
-
3312
- # Try deleting all devices and then editing the 6 VLANs (fails):
3313
- Controller.objects.filter(controller_device__isnull=False).delete()
3314
- Device.objects.all().delete()
3315
- response = self.client.post(
3316
- reverse("ipam:vlan_bulk_edit"), data={"pk": [str(vlan.id) for vlan in vlans], "_apply": [""]}
3317
- )
3318
- self.assertContains(response, "VLANs require at least one device, but no devices exist yet.")
3319
-
3320
- # Create test device for association
3321
- device_for_association = test_views.create_test_device("VLAN Required Device")
3322
-
3323
- # Try editing all 6 VLANs without adding the required device(fails):
3324
- response = self.client.post(
3325
- reverse("ipam:vlan_bulk_edit"), data={"pk": [str(vlan.id) for vlan in vlans], "_apply": [""]}
3326
- )
3327
- self.assertContains(
3328
- response,
3329
- "6 VLANs require a device for the required relationship &quot;VLANs require at least one Device&quot;",
3330
- )
3331
-
3332
- # Try editing 3 VLANs without adding the required device(fails):
3333
- response = self.client.post(
3334
- reverse("ipam:vlan_bulk_edit"), data={"pk": [str(vlan.id) for vlan in vlans[:3]], "_apply": [""]}
3335
- )
3336
- self.assertContains(
3337
- response,
3338
- "These VLANs require a device for the required "
3339
- "relationship &quot;VLANs require at least one Device&quot;",
3340
- )
3341
- for vlan in vlans[:3]:
3342
- self.assertContains(response, str(vlan))
3343
-
3344
- # Try editing 6 VLANs and adding the required device (succeeds):
3345
- response = self.client.post(
3346
- reverse("ipam:vlan_bulk_edit"),
3347
- data={
3348
- "pk": [str(vlan.id) for vlan in vlans],
3349
- "add_cr_vlans_devices_m2m__source": [str(device_for_association.id)],
3350
- "_apply": [""],
3351
- },
3352
- follow=True,
3353
- )
3354
- self.assertContains(response, "Updated 6 VLANs")
3355
-
3356
- # Try editing 6 VLANs and removing the required device (fails):
3357
- response = self.client.post(
3358
- reverse("ipam:vlan_bulk_edit"),
3359
- data={
3360
- "pk": [str(vlan.id) for vlan in vlans],
3361
- "remove_cr_vlans_devices_m2m__source": [str(device_for_association.id)],
3362
- "_apply": [""],
3363
- },
3364
- )
3365
- self.assertContains(
3366
- response,
3367
- "6 VLANs require a device for the required relationship &quot;VLANs require at least one Device&quot;",
3368
- )
3369
-
3370
3230
 
3371
3231
  class RelationshipAssociationTestCase(
3372
3232
  # TODO? ViewTestCases.CreateObjectViewTestCase,
@@ -3749,6 +3609,8 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
3749
3609
 
3750
3610
  cls.bulk_edit_data = {
3751
3611
  "color": "000000",
3612
+ "description": "I used to be a new role object.",
3613
+ "weight": 255,
3752
3614
  }
3753
3615
 
3754
3616
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
nautobot/extras/utils.py CHANGED
@@ -579,6 +579,7 @@ def refresh_job_model_from_job_class(job_model_class, job_class, job_queue_class
579
579
  "installed": True,
580
580
  "enabled": False,
581
581
  "default_job_queue": default_job_queue,
582
+ "is_singleton": job_class.is_singleton,
582
583
  },
583
584
  )
584
585
 
@@ -665,9 +666,9 @@ def run_kubernetes_job_and_return_job_result(job_queue, job_result, job_kwargs):
665
666
  "runjob_with_job_result",
666
667
  f"{job_result.pk}",
667
668
  ]
668
- logger.info("Creating job pod %s in namespace %s", pod_name, pod_namespace)
669
+ job_result.log(f"Creating job pod {pod_name} in namespace {pod_namespace}")
669
670
  api_instance.create_namespaced_job(body=pod_manifest, namespace=pod_namespace)
670
- logger.info("Reading job pod %s in namespace %s", pod_name, pod_namespace)
671
+ job_result.log(f"Reading job pod {pod_name} in namespace {pod_namespace}")
671
672
  api_instance.read_namespaced_job(name="nautobot-job-" + str(job_result.pk), namespace=pod_namespace)
672
673
  return job_result
673
674