nautobot 2.4.0__py3-none-any.whl → 2.4.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.

Potentially problematic release.


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

Files changed (592) 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 +0 -1
  18. nautobot/circuits/api/views.py +8 -4
  19. nautobot/circuits/tables.py +1 -2
  20. nautobot/circuits/templates/circuits/circuit_create.html +7 -1
  21. nautobot/circuits/views.py +3 -3
  22. nautobot/cloud/api/views.py +10 -6
  23. nautobot/cloud/models.py +1 -1
  24. nautobot/cloud/views.py +16 -0
  25. nautobot/core/api/fields.py +5 -5
  26. nautobot/core/api/filter_backends.py +9 -3
  27. nautobot/core/api/schema.py +2 -13
  28. nautobot/core/api/serializers.py +34 -40
  29. nautobot/core/api/views.py +4 -56
  30. nautobot/core/celery/log.py +4 -4
  31. nautobot/core/celery/schedulers.py +1 -1
  32. nautobot/core/choices.py +2 -2
  33. nautobot/core/events/__init__.py +3 -3
  34. nautobot/core/filters.py +16 -21
  35. nautobot/core/fixtures/user-data.json +59 -0
  36. nautobot/core/forms/__init__.py +19 -19
  37. nautobot/core/forms/fields.py +11 -14
  38. nautobot/core/forms/forms.py +2 -33
  39. nautobot/core/graphql/types.py +1 -1
  40. nautobot/core/jobs/__init__.py +7 -28
  41. nautobot/core/jobs/cleanup.py +12 -48
  42. nautobot/core/jobs/groups.py +1 -1
  43. nautobot/core/management/commands/validate_models.py +1 -1
  44. nautobot/core/models/__init__.py +1 -1
  45. nautobot/core/models/query_functions.py +2 -2
  46. nautobot/core/models/tree_queries.py +3 -6
  47. nautobot/core/settings.py +2 -29
  48. nautobot/core/settings.yaml +0 -21
  49. nautobot/core/tables.py +60 -74
  50. nautobot/core/templates/inc/media.html +0 -3
  51. nautobot/core/templates/inc/nav_menu.html +0 -1
  52. nautobot/core/templates/nautobot_config.py.j2 +0 -13
  53. nautobot/core/templates/search.html +0 -7
  54. nautobot/core/templates/utilities/render_jinja2.html +1 -1
  55. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  56. nautobot/core/templates/utilities/theme_preview.html +0 -7
  57. nautobot/core/templatetags/helpers.py +2 -11
  58. nautobot/core/testing/__init__.py +8 -8
  59. nautobot/core/testing/api.py +15 -170
  60. nautobot/core/testing/filters.py +2 -25
  61. nautobot/core/testing/forms.py +0 -2
  62. nautobot/core/testing/mixins.py +2 -7
  63. nautobot/core/testing/views.py +29 -44
  64. nautobot/core/tests/integration/test_app_home.py +1 -0
  65. nautobot/core/tests/integration/test_app_navbar.py +1 -0
  66. nautobot/core/tests/integration/test_filters.py +2 -0
  67. nautobot/core/tests/integration/test_home.py +1 -0
  68. nautobot/core/tests/integration/test_navbar.py +1 -0
  69. nautobot/core/tests/integration/test_view_authentication.py +0 -1
  70. nautobot/core/tests/runner.py +1 -1
  71. nautobot/core/tests/test_api.py +1 -98
  72. nautobot/core/tests/test_csv.py +3 -25
  73. nautobot/core/tests/test_forms.py +0 -1
  74. nautobot/core/tests/test_jobs.py +1 -303
  75. nautobot/core/tests/test_settings_schema.py +0 -7
  76. nautobot/core/tests/test_tables.py +0 -100
  77. nautobot/core/tests/test_utils.py +1 -63
  78. nautobot/core/tests/test_views.py +3 -30
  79. nautobot/core/ui/nav.py +0 -1
  80. nautobot/core/ui/object_detail.py +1 -15
  81. nautobot/core/urls.py +0 -11
  82. nautobot/core/utils/lookup.py +8 -11
  83. nautobot/core/utils/requests.py +9 -24
  84. nautobot/core/views/__init__.py +0 -42
  85. nautobot/core/views/generic.py +176 -78
  86. nautobot/core/views/mixins.py +34 -94
  87. nautobot/core/views/renderers.py +6 -6
  88. nautobot/dcim/api/serializers.py +62 -54
  89. nautobot/dcim/api/views.py +113 -47
  90. nautobot/dcim/filters/__init__.py +2 -31
  91. nautobot/dcim/forms.py +17 -36
  92. nautobot/dcim/graphql/types.py +2 -2
  93. nautobot/dcim/models/__init__.py +1 -1
  94. nautobot/dcim/models/device_component_templates.py +2 -2
  95. nautobot/dcim/models/device_components.py +20 -22
  96. nautobot/dcim/models/devices.py +1 -10
  97. nautobot/dcim/models/locations.py +3 -3
  98. nautobot/dcim/models/power.py +5 -6
  99. nautobot/dcim/models/racks.py +4 -4
  100. nautobot/dcim/tables/__init__.py +3 -3
  101. nautobot/dcim/tables/devices.py +5 -7
  102. nautobot/dcim/tables/devicetypes.py +2 -2
  103. nautobot/dcim/tables/racks.py +1 -1
  104. nautobot/dcim/templates/dcim/controller_create.html +7 -1
  105. nautobot/dcim/templates/dcim/controller_retrieve.html +9 -1
  106. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +0 -2
  107. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -5
  108. nautobot/dcim/templates/dcim/device.html +9 -1
  109. nautobot/dcim/templates/dcim/device_edit.html +37 -36
  110. nautobot/dcim/templates/dcim/location.html +9 -1
  111. nautobot/dcim/templates/dcim/location_edit.html +7 -1
  112. nautobot/dcim/templates/dcim/rack.html +9 -1
  113. nautobot/dcim/templates/dcim/rack_edit.html +7 -1
  114. nautobot/dcim/templates/dcim/rackreservation.html +9 -1
  115. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +9 -1
  116. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +7 -1
  117. nautobot/dcim/tests/test_api.py +3 -16
  118. nautobot/dcim/tests/test_filters.py +0 -33
  119. nautobot/dcim/tests/test_forms.py +2 -51
  120. nautobot/dcim/tests/test_graphql.py +0 -52
  121. nautobot/dcim/tests/test_models.py +5 -34
  122. nautobot/dcim/tests/test_views.py +83 -21
  123. nautobot/dcim/views.py +13 -1
  124. nautobot/extras/api/customfields.py +2 -2
  125. nautobot/extras/api/serializers.py +85 -90
  126. nautobot/extras/api/views.py +27 -22
  127. nautobot/extras/constants.py +0 -2
  128. nautobot/extras/filters/__init__.py +6 -8
  129. nautobot/extras/forms/base.py +2 -2
  130. nautobot/extras/forms/forms.py +31 -139
  131. nautobot/extras/forms/mixins.py +5 -13
  132. nautobot/extras/group_sync.py +3 -3
  133. nautobot/extras/health_checks.py +2 -1
  134. nautobot/extras/jobs.py +12 -70
  135. nautobot/extras/managers.py +1 -3
  136. nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
  137. nautobot/extras/models/__init__.py +1 -1
  138. nautobot/extras/models/contacts.py +1 -1
  139. nautobot/extras/models/customfields.py +11 -12
  140. nautobot/extras/models/groups.py +9 -11
  141. nautobot/extras/models/jobs.py +4 -23
  142. nautobot/extras/models/models.py +2 -2
  143. nautobot/extras/plugins/__init__.py +2 -13
  144. nautobot/extras/plugins/marketplace_manifest.yml +79 -84
  145. nautobot/extras/plugins/tables.py +14 -16
  146. nautobot/extras/plugins/views.py +69 -65
  147. nautobot/extras/registry.py +1 -1
  148. nautobot/extras/secrets/__init__.py +2 -2
  149. nautobot/extras/tables.py +5 -7
  150. nautobot/extras/templates/extras/dynamicgroup.html +9 -1
  151. nautobot/extras/templates/extras/job_detail.html +0 -16
  152. nautobot/extras/templates/extras/job_edit.html +0 -1
  153. nautobot/extras/templates/extras/jobqueue_retrieve.html +9 -1
  154. nautobot/extras/templates/extras/marketplace.html +11 -29
  155. nautobot/extras/templates/extras/plugin_detail.html +15 -32
  156. nautobot/extras/templates/extras/plugins_tiles.html +10 -21
  157. nautobot/extras/test_jobs/api_test_job.py +1 -1
  158. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  159. nautobot/extras/test_jobs/dry_run.py +1 -1
  160. nautobot/extras/test_jobs/fail.py +5 -5
  161. nautobot/extras/test_jobs/file_output.py +1 -1
  162. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  163. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  164. nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
  165. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  166. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  167. nautobot/extras/test_jobs/log_redaction.py +1 -1
  168. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  169. nautobot/extras/test_jobs/modify_db.py +1 -1
  170. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  171. nautobot/extras/test_jobs/object_var_required.py +1 -1
  172. nautobot/extras/test_jobs/object_vars.py +1 -1
  173. nautobot/extras/test_jobs/pass.py +3 -3
  174. nautobot/extras/test_jobs/profiling.py +1 -1
  175. nautobot/extras/test_jobs/relative_import.py +3 -3
  176. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  177. nautobot/extras/test_jobs/task_queues.py +1 -1
  178. nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
  179. nautobot/extras/tests/test_api.py +13 -13
  180. nautobot/extras/tests/test_customfields.py +1 -1
  181. nautobot/extras/tests/test_datasources.py +1 -2
  182. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  183. nautobot/extras/tests/test_filters.py +6 -6
  184. nautobot/extras/tests/test_forms.py +1 -20
  185. nautobot/extras/tests/test_jobs.py +19 -160
  186. nautobot/extras/tests/test_models.py +10 -10
  187. nautobot/extras/tests/test_plugins.py +9 -62
  188. nautobot/extras/tests/test_relationships.py +9 -120
  189. nautobot/extras/tests/test_views.py +191 -52
  190. nautobot/extras/utils.py +2 -3
  191. nautobot/extras/views.py +98 -30
  192. nautobot/ipam/api/fields.py +3 -3
  193. nautobot/ipam/api/serializers.py +33 -41
  194. nautobot/ipam/api/views.py +117 -68
  195. nautobot/ipam/factory.py +1 -1
  196. nautobot/ipam/filters.py +2 -3
  197. nautobot/ipam/lookups.py +62 -101
  198. nautobot/ipam/models.py +16 -66
  199. nautobot/ipam/querysets.py +2 -2
  200. nautobot/ipam/tables.py +7 -23
  201. nautobot/ipam/templates/ipam/ipaddress.html +9 -1
  202. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +7 -1
  203. nautobot/ipam/templates/ipam/ipaddress_edit.html +7 -1
  204. nautobot/ipam/templates/ipam/prefix.html +9 -1
  205. nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
  206. nautobot/ipam/templates/ipam/vlan.html +9 -1
  207. nautobot/ipam/templates/ipam/vlan_edit.html +7 -1
  208. nautobot/ipam/templates/ipam/vrf_edit.html +7 -1
  209. nautobot/ipam/tests/test_api.py +3 -416
  210. nautobot/ipam/tests/test_forms.py +47 -49
  211. nautobot/ipam/tests/test_migrations.py +30 -30
  212. nautobot/ipam/tests/test_models.py +34 -95
  213. nautobot/ipam/tests/test_querysets.py +1 -63
  214. nautobot/ipam/tests/test_views.py +0 -3
  215. nautobot/ipam/utils/__init__.py +6 -36
  216. nautobot/ipam/views.py +87 -61
  217. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  218. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  219. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +2 -40
  220. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  221. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  222. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  223. nautobot/project-static/docs/404.html +4 -46
  224. nautobot/project-static/docs/apps/index.html +4 -46
  225. nautobot/project-static/docs/apps/nautobot-apps.html +6 -47
  226. nautobot/project-static/docs/assets/_mkdocstrings.css +1 -25
  227. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
  228. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
  229. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +10 -62
  230. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +7 -59
  231. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +122 -374
  232. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +18 -90
  233. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +21 -95
  234. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +6 -53
  235. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +5 -52
  236. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +17 -79
  237. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +28 -102
  238. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +21 -108
  239. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +38 -131
  240. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +65 -239
  241. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +165 -581
  242. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +36 -109
  243. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +167 -453
  244. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +211 -493
  245. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +8 -60
  246. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +15 -71
  247. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +55 -407
  248. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +205 -585
  249. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +412 -858
  250. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +7 -59
  251. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +186 -448
  252. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +147 -365
  253. nautobot/project-static/docs/development/apps/api/configuration-view.html +4 -46
  254. nautobot/project-static/docs/development/apps/api/database-backend-config.html +4 -46
  255. nautobot/project-static/docs/development/apps/api/models/django-admin.html +4 -46
  256. nautobot/project-static/docs/development/apps/api/models/global-search.html +4 -46
  257. nautobot/project-static/docs/development/apps/api/models/graphql.html +4 -46
  258. nautobot/project-static/docs/development/apps/api/models/index.html +4 -46
  259. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +4 -46
  260. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +4 -46
  261. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +4 -46
  262. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +4 -46
  263. nautobot/project-static/docs/development/apps/api/platform-features/index.html +4 -46
  264. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +4 -46
  265. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +4 -46
  266. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +4 -46
  267. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +4 -46
  268. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +7 -68
  269. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +4 -46
  270. nautobot/project-static/docs/development/apps/api/prometheus.html +4 -46
  271. nautobot/project-static/docs/development/apps/api/setup.html +4 -46
  272. nautobot/project-static/docs/development/apps/api/testing.html +4 -46
  273. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +4 -46
  274. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +4 -46
  275. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +4 -46
  276. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +4 -46
  277. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +4 -46
  278. nautobot/project-static/docs/development/apps/api/views/base-template.html +4 -46
  279. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +4 -46
  280. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +4 -46
  281. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +4 -46
  282. nautobot/project-static/docs/development/apps/api/views/index.html +4 -46
  283. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +4 -46
  284. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +4 -46
  285. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +4 -46
  286. nautobot/project-static/docs/development/apps/api/views/notes.html +4 -46
  287. nautobot/project-static/docs/development/apps/api/views/rest-api.html +6 -52
  288. nautobot/project-static/docs/development/apps/api/views/urls.html +4 -46
  289. nautobot/project-static/docs/development/apps/index.html +4 -46
  290. nautobot/project-static/docs/development/apps/migration/code-updates.html +4 -46
  291. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +4 -46
  292. nautobot/project-static/docs/development/apps/migration/from-v1.html +4 -46
  293. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +4 -46
  294. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +4 -46
  295. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +4 -46
  296. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +4 -46
  297. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +8 -50
  298. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +4 -46
  299. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +14 -211
  300. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +4 -46
  301. nautobot/project-static/docs/development/apps/porting-from-netbox.html +4 -46
  302. nautobot/project-static/docs/development/core/application-registry.html +4 -46
  303. nautobot/project-static/docs/development/core/best-practices.html +4 -46
  304. nautobot/project-static/docs/development/core/bootstrap-ui.html +4 -46
  305. nautobot/project-static/docs/development/core/caching.html +4 -46
  306. nautobot/project-static/docs/development/core/controllers.html +4 -46
  307. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +74 -73
  308. nautobot/project-static/docs/development/core/generic-views.html +4 -46
  309. nautobot/project-static/docs/development/core/getting-started.html +224 -249
  310. nautobot/project-static/docs/development/core/homepage.html +7 -49
  311. nautobot/project-static/docs/development/core/index.html +4 -46
  312. nautobot/project-static/docs/development/core/{minikube-dev-environment-for-k8s-jobs.html → local-k8s.html} +168 -469
  313. nautobot/project-static/docs/development/core/model-checklist.html +12 -56
  314. nautobot/project-static/docs/development/core/model-features.html +4 -46
  315. nautobot/project-static/docs/development/core/natural-keys.html +4 -46
  316. nautobot/project-static/docs/development/core/navigation-menu.html +4 -46
  317. nautobot/project-static/docs/development/core/release-checklist.html +7 -49
  318. nautobot/project-static/docs/development/core/role-internals.html +4 -46
  319. nautobot/project-static/docs/development/core/settings.html +4 -46
  320. nautobot/project-static/docs/development/core/style-guide.html +7 -49
  321. nautobot/project-static/docs/development/core/templates.html +4 -46
  322. nautobot/project-static/docs/development/core/testing.html +4 -46
  323. nautobot/project-static/docs/development/core/ui-component-framework.html +273 -369
  324. nautobot/project-static/docs/development/core/user-preferences.html +4 -46
  325. nautobot/project-static/docs/development/index.html +4 -46
  326. nautobot/project-static/docs/development/jobs/index.html +122 -216
  327. nautobot/project-static/docs/development/jobs/migration/from-v1.html +4 -46
  328. nautobot/project-static/docs/index.html +23 -54
  329. nautobot/project-static/docs/objects.inv +0 -0
  330. nautobot/project-static/docs/overview/application_stack.html +7 -47
  331. nautobot/project-static/docs/overview/design_philosophy.html +4 -46
  332. nautobot/project-static/docs/release-notes/index.html +12 -52
  333. nautobot/project-static/docs/release-notes/version-1.0.html +193 -234
  334. nautobot/project-static/docs/release-notes/version-1.1.html +190 -231
  335. nautobot/project-static/docs/release-notes/version-1.2.html +265 -306
  336. nautobot/project-static/docs/release-notes/version-1.3.html +291 -332
  337. nautobot/project-static/docs/release-notes/version-1.4.html +377 -417
  338. nautobot/project-static/docs/release-notes/version-1.5.html +566 -605
  339. nautobot/project-static/docs/release-notes/version-1.6.html +447 -904
  340. nautobot/project-static/docs/release-notes/version-2.0.html +489 -528
  341. nautobot/project-static/docs/release-notes/version-2.1.html +324 -363
  342. nautobot/project-static/docs/release-notes/version-2.2.html +317 -356
  343. nautobot/project-static/docs/release-notes/version-2.3.html +352 -997
  344. nautobot/project-static/docs/release-notes/version-2.4.html +101 -417
  345. nautobot/project-static/docs/requirements.txt +2 -2
  346. nautobot/project-static/docs/search/search_index.json +1 -1
  347. nautobot/project-static/docs/sitemap.xml +287 -295
  348. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  349. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +4 -46
  350. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +4 -46
  351. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +6 -48
  352. nautobot/project-static/docs/user-guide/administration/configuration/index.html +4 -46
  353. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +4 -46
  354. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +8 -110
  355. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +4 -46
  356. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +4 -46
  357. nautobot/project-static/docs/user-guide/administration/guides/docker.html +4 -46
  358. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +4 -46
  359. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +4 -46
  360. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +4 -46
  361. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +4 -46
  362. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +4 -46
  363. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +6 -48
  364. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +4 -46
  365. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +4 -46
  366. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +4 -46
  367. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +8 -66
  368. nautobot/project-static/docs/user-guide/administration/installation/index.html +4 -46
  369. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +5 -47
  370. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +4 -46
  371. nautobot/project-static/docs/user-guide/administration/installation/services.html +4 -46
  372. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +4 -46
  373. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +4 -46
  374. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +4 -46
  375. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +4 -46
  376. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +4 -46
  377. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +4 -46
  378. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +4 -46
  379. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +4 -46
  380. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +4 -46
  381. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +8 -49
  382. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +4 -46
  383. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +4 -46
  384. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +4 -46
  385. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +4 -46
  386. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +4 -46
  387. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +4 -46
  388. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +4 -46
  389. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +4 -46
  390. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +4 -46
  391. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +4 -46
  392. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +4 -46
  393. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +4 -46
  394. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +4 -46
  395. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +4 -46
  396. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +4 -46
  397. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +4 -46
  398. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +4 -46
  399. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +4 -46
  400. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +4 -46
  401. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +4 -46
  402. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +4 -46
  403. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +4 -46
  404. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +4 -46
  405. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +4 -46
  406. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +4 -46
  407. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +4 -46
  408. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +4 -46
  409. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +4 -46
  410. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +4 -46
  411. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +4 -46
  412. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +4 -46
  413. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +4 -46
  414. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +4 -46
  415. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +4 -46
  416. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +4 -46
  417. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +4 -46
  418. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +4 -46
  419. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +4 -46
  420. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +4 -46
  421. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +4 -46
  422. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +4 -46
  423. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +4 -46
  424. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +4 -46
  425. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +4 -46
  426. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +4 -46
  427. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +4 -46
  428. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +4 -46
  429. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +4 -46
  430. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +4 -46
  431. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +4 -46
  432. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +4 -46
  433. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +4 -46
  434. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +4 -46
  435. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -46
  436. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +4 -46
  437. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +4 -46
  438. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +4 -46
  439. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +12 -50
  440. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +4 -46
  441. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +4 -46
  442. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +4 -46
  443. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +4 -46
  444. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +4 -46
  445. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +4 -46
  446. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +4 -46
  447. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +4 -46
  448. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +4 -46
  449. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +4 -46
  450. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +4 -46
  451. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +4 -46
  452. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +7 -49
  453. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +4 -46
  454. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +4 -46
  455. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +4 -46
  456. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +4 -46
  457. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +4 -46
  458. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +4 -46
  459. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +4 -46
  460. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +4 -46
  461. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +4 -46
  462. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +4 -46
  463. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +4 -46
  464. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +4 -46
  465. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +4 -46
  466. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +4 -46
  467. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +4 -46
  468. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +4 -46
  469. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +4 -46
  470. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -46
  471. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +4 -46
  472. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +4 -46
  473. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +4 -46
  474. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +4 -46
  475. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +7 -51
  476. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +4 -46
  477. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +4 -46
  478. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +4 -46
  479. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +7 -49
  480. nautobot/project-static/docs/user-guide/index.html +4 -46
  481. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +4 -46
  482. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +8 -50
  483. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +4 -46
  484. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +4 -46
  485. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +4 -46
  486. nautobot/project-static/docs/user-guide/platform-functionality/events.html +4 -46
  487. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +4 -46
  488. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +4 -46
  489. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +4 -46
  490. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +4 -46
  491. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +4 -46
  492. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +4 -46
  493. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +7 -50
  494. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +4 -46
  495. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +7 -49
  496. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +4 -46
  497. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +4 -46
  498. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +4 -46
  499. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +4 -46
  500. nautobot/project-static/docs/user-guide/platform-functionality/note.html +4 -46
  501. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +4 -46
  502. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +5 -47
  503. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +4 -46
  504. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +4 -46
  505. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +25 -94
  506. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +5 -74
  507. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +4 -46
  508. nautobot/project-static/docs/user-guide/platform-functionality/role.html +4 -46
  509. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +4 -46
  510. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +4 -46
  511. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +4 -46
  512. nautobot/project-static/docs/user-guide/platform-functionality/status.html +4 -46
  513. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +4 -46
  514. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +4 -46
  515. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +4 -46
  516. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +4 -46
  517. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +4 -46
  518. nautobot/project-static/js/forms.js +1 -1
  519. nautobot/tenancy/api/views.py +13 -9
  520. nautobot/tenancy/views.py +2 -4
  521. nautobot/users/admin.py +1 -1
  522. nautobot/users/api/serializers.py +4 -5
  523. nautobot/users/api/views.py +3 -3
  524. nautobot/virtualization/api/serializers.py +4 -4
  525. nautobot/virtualization/api/views.py +24 -5
  526. nautobot/virtualization/filters.py +3 -20
  527. nautobot/virtualization/models.py +1 -1
  528. nautobot/virtualization/tables.py +2 -2
  529. nautobot/virtualization/templates/virtualization/cluster_edit.html +7 -1
  530. nautobot/virtualization/templates/virtualization/virtualmachine.html +9 -1
  531. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +8 -2
  532. nautobot/virtualization/tests/test_filters.py +0 -17
  533. nautobot/wireless/filters.py +2 -2
  534. nautobot/wireless/forms.py +1 -1
  535. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +9 -1
  536. nautobot/wireless/tests/test_filters.py +1 -29
  537. nautobot/wireless/views.py +10 -0
  538. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +6 -6
  539. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +543 -591
  540. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
  541. nautobot/core/api/constants.py +0 -11
  542. nautobot/core/jobs/bulk_actions.py +0 -248
  543. nautobot/core/templates/about.html +0 -67
  544. nautobot/core/templates/inc/tenancy_form_panel.html +0 -9
  545. nautobot/core/templates/inc/tenant_table_row.html +0 -11
  546. nautobot/core/utils/querysets.py +0 -64
  547. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +0 -25
  548. nautobot/dcim/tests/integration/test_controller.py +0 -62
  549. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +0 -71
  550. nautobot/dcim/tests/test_jobs.py +0 -118
  551. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +0 -22
  552. nautobot/extras/migrations/0121_alter_team_contacts.py +0 -17
  553. nautobot/extras/test_jobs/singleton.py +0 -16
  554. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  555. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  556. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  557. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  558. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  559. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  560. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  561. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  562. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  563. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  564. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  565. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  566. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  567. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  568. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  569. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  570. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  571. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  572. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  573. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  574. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  575. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  576. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -9444
  587. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -9722
  588. nautobot/wireless/tests/integration/__init__.py +0 -0
  589. nautobot/wireless/tests/integration/test_radio_profile.py +0 -42
  590. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
  591. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
  592. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.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 create_job_result_and_run_job, TestCase, TransactionTestCase
15
+ from nautobot.core.testing import TestCase
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,22 +29,9 @@ 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 (
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
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
48
35
 
49
36
 
50
37
  class RelationshipBaseTest:
@@ -367,24 +354,20 @@ class RelationshipTest(RelationshipBaseTest, ModelTestCases.BaseModelTestCase):
367
354
  self.assertFalse(field.required)
368
355
  self.assertIsInstance(field, DynamicModelMultipleChoiceField)
369
356
  self.assertEqual(field.label, "My VLANs")
370
- self.assertEqual(field.query_params, {"exclude_m2m": "true"})
357
+ self.assertEqual(field.query_params, {})
371
358
 
372
359
  field = self.m2m_1.to_form_field("destination")
373
360
  self.assertFalse(field.required)
374
361
  self.assertIsInstance(field, DynamicModelMultipleChoiceField)
375
362
  self.assertEqual(field.label, "My Racks")
376
363
  self.assertEqual(
377
- field.query_params,
378
- {
379
- "location": [self.locations[0].name, self.locations[1].name, self.locations[2].name],
380
- "exclude_m2m": "true",
381
- },
364
+ field.query_params, {"location": [self.locations[0].name, self.locations[1].name, self.locations[2].name]}
382
365
  )
383
366
 
384
367
  field = self.m2ms_1.to_form_field("peer")
385
368
  self.assertFalse(field.required)
386
369
  self.assertIsInstance(field, DynamicModelMultipleChoiceField)
387
- self.assertEqual(field.query_params, {"exclude_m2m": "true"})
370
+ self.assertEqual(field.query_params, {})
388
371
 
389
372
  def test_to_form_field_o2m(self):
390
373
  field = self.o2m_1.to_form_field("source")
@@ -1343,7 +1326,7 @@ class RelationshipTableTest(RelationshipBaseTest, TestCase):
1343
1326
  self.assertIsNotNone(relationship_column)
1344
1327
  self.assertIsInstance(relationship_column, RelationshipColumn)
1345
1328
 
1346
- rendered_value = bound_row.get_cell(internal_col_name) # pylint: disable=no-member
1329
+ rendered_value = bound_row.get_cell(internal_col_name)
1347
1330
  # Test if the expected value is in the rendered value.
1348
1331
  # Exact match is difficult because the order of rendering is unpredictable.
1349
1332
  for value in col_expected_value:
@@ -1449,7 +1432,7 @@ class RequiredRelationshipTestMixin:
1449
1432
  required_on="source",
1450
1433
  )
1451
1434
  relationship_o2o.validated_save()
1452
- vlan_group = VLANGroup.objects.create(name="Test VLANGroup 1")
1435
+ vlan_group = VLANGroup.objects.first()
1453
1436
 
1454
1437
  tests_params = [
1455
1438
  # Required many-to-many:
@@ -1729,97 +1712,3 @@ class RequiredRelationshipTestMixin:
1729
1712
  }
1730
1713
  }
1731
1714
  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,4 +1,5 @@
1
1
  from datetime import timedelta
2
+ from http import HTTPStatus
2
3
  import json
3
4
  from unittest import mock
4
5
  import urllib.parse
@@ -25,6 +26,7 @@ from nautobot.core.testing.utils import disable_warnings, get_deletable_objects,
25
26
  from nautobot.core.utils.permissions import get_permission_for_model
26
27
  from nautobot.dcim.models import (
27
28
  ConsolePort,
29
+ Controller,
28
30
  Device,
29
31
  DeviceType,
30
32
  Interface,
@@ -32,6 +34,7 @@ from nautobot.dcim.models import (
32
34
  LocationType,
33
35
  Manufacturer,
34
36
  )
37
+ from nautobot.dcim.tests import test_views
35
38
  from nautobot.extras.choices import (
36
39
  CustomFieldTypeChoices,
37
40
  DynamicGroupTypeChoices,
@@ -86,9 +89,10 @@ from nautobot.extras.templatetags.job_buttons import NO_CONFIRM_BUTTON
86
89
  from nautobot.extras.tests.constants import BIG_GRAPHQL_DEVICE_QUERY
87
90
  from nautobot.extras.tests.test_relationships import RequiredRelationshipTestMixin
88
91
  from nautobot.extras.utils import RoleModelsQuery, TaggableClassesQuery
89
- from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup, VRF
92
+ from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
90
93
  from nautobot.tenancy.models import Tenant
91
94
  from nautobot.users.models import ObjectPermission
95
+ from nautobot.wireless.models import ControllerManagedDeviceGroupWirelessNetworkAssignment
92
96
 
93
97
  # Use the proper swappable User model
94
98
  User = get_user_model()
@@ -911,30 +915,6 @@ class DynamicGroupTestCase(
911
915
  self.form_data["content_type"] = instance.content_type.pk # Content-type is not editable after creation
912
916
  super().test_edit_object_with_constrained_permission()
913
917
 
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
-
938
918
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
939
919
  def test_edit_saved_filter(self):
940
920
  """Test that editing a filter works using the edit view."""
@@ -1718,21 +1698,21 @@ class ScheduledJobTestCase(
1718
1698
  user = User.objects.create(username="user1", is_active=True)
1719
1699
  ScheduledJob.objects.create(
1720
1700
  name="test1",
1721
- task="pass.TestPassJob",
1701
+ task="pass.TestPass",
1722
1702
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1723
1703
  user=user,
1724
1704
  start_time=timezone.now(),
1725
1705
  )
1726
1706
  ScheduledJob.objects.create(
1727
1707
  name="test2",
1728
- task="pass.TestPassJob",
1708
+ task="pass.TestPass",
1729
1709
  interval=JobExecutionType.TYPE_DAILY,
1730
1710
  user=user,
1731
1711
  start_time=timezone.now(),
1732
1712
  )
1733
1713
  ScheduledJob.objects.create(
1734
1714
  name="test3",
1735
- task="pass.TestPassJob",
1715
+ task="pass.TestPass",
1736
1716
  interval=JobExecutionType.TYPE_CUSTOM,
1737
1717
  user=user,
1738
1718
  start_time=timezone.now(),
@@ -1746,7 +1726,7 @@ class ScheduledJobTestCase(
1746
1726
  ScheduledJob.objects.create(
1747
1727
  enabled=False,
1748
1728
  name="test4",
1749
- task="pass.TestPassJob",
1729
+ task="pass.TestPass",
1750
1730
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1751
1731
  user=self.user,
1752
1732
  start_time=timezone.now(),
@@ -1763,7 +1743,7 @@ class ScheduledJobTestCase(
1763
1743
  ScheduledJob.objects.create(
1764
1744
  enabled=True,
1765
1745
  name=name,
1766
- task="pass.TestPassJob",
1746
+ task="pass.TestPass",
1767
1747
  interval=JobExecutionType.TYPE_CUSTOM,
1768
1748
  user=self.user,
1769
1749
  start_time=timezone.now(),
@@ -1794,7 +1774,7 @@ class ScheduledJobTestCase(
1794
1774
  ScheduledJob.objects.create(
1795
1775
  enabled=True,
1796
1776
  name="test11",
1797
- task="pass.TestPassJob",
1777
+ task="pass.TestPass",
1798
1778
  interval=JobExecutionType.TYPE_CUSTOM,
1799
1779
  user=self.user,
1800
1780
  start_time=timezone.now(),
@@ -1828,7 +1808,7 @@ class ApprovalQueueTestCase(
1828
1808
  def setUp(self):
1829
1809
  super().setUp()
1830
1810
  self.job_model = Job.objects.get_for_class_path("dry_run.TestDryRun")
1831
- self.job_model_2 = Job.objects.get_for_class_path("fail.TestFailJob")
1811
+ self.job_model_2 = Job.objects.get_for_class_path("fail.TestFail")
1832
1812
 
1833
1813
  ScheduledJob.objects.create(
1834
1814
  name="test1",
@@ -1841,7 +1821,7 @@ class ApprovalQueueTestCase(
1841
1821
  )
1842
1822
  ScheduledJob.objects.create(
1843
1823
  name="test2",
1844
- task="fail.TestFailJob",
1824
+ task="fail.TestFail",
1845
1825
  job_model=self.job_model_2,
1846
1826
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1847
1827
  user=self.user,
@@ -1854,7 +1834,7 @@ class ApprovalQueueTestCase(
1854
1834
 
1855
1835
  ScheduledJob.objects.create(
1856
1836
  name="test4",
1857
- task="pass.TestPassJob",
1837
+ task="pass.TestPass",
1858
1838
  job_model=self.job_model,
1859
1839
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1860
1840
  user=self.user,
@@ -2245,8 +2225,8 @@ class JobResultTestCase(
2245
2225
 
2246
2226
  @classmethod
2247
2227
  def setUpTestData(cls):
2248
- JobResult.objects.create(name="pass.TestPassJob")
2249
- JobResult.objects.create(name="fail.TestFailJob")
2228
+ JobResult.objects.create(name="pass.TestPass")
2229
+ JobResult.objects.create(name="fail.TestFail")
2250
2230
  JobLogEntry.objects.create(
2251
2231
  log_level=LogLevelChoices.LOG_INFO,
2252
2232
  job_result=JobResult.objects.first(),
@@ -2300,7 +2280,7 @@ class JobTestCase(
2300
2280
  # Job model objects are automatically created during database migrations
2301
2281
 
2302
2282
  # But we do need to make sure the ones we're testing are flagged appropriately
2303
- cls.test_pass = Job.objects.get(job_class_name="TestPassJob")
2283
+ cls.test_pass = Job.objects.get(job_class_name="TestPass")
2304
2284
  default_job_queue = JobQueue.objects.get(name="default", queue_type=JobQueueTypeChoices.TYPE_CELERY)
2305
2285
  cls.test_pass.default_job_queue = default_job_queue
2306
2286
  cls.test_pass.enabled = True
@@ -2436,6 +2416,35 @@ class JobTestCase(
2436
2416
  # assert Job still exists
2437
2417
  self.assertTrue(self._get_queryset().filter(name=job_name).exists())
2438
2418
 
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
+
2439
2448
  def validate_job_data_after_bulk_edit(self, pk_list, old_data):
2440
2449
  # Name is bulk-editable
2441
2450
  overridable_fields = [field for field in JOB_OVERRIDABLE_FIELDS if field != "name"]
@@ -2504,7 +2513,7 @@ class JobTestCase(
2504
2513
  self.add_permissions("extras.run_job")
2505
2514
  for run_url in self.run_urls:
2506
2515
  response = self.client.get(run_url)
2507
- self.assertBodyContains(response, "TestPassJob")
2516
+ self.assertBodyContains(response, "TestPass")
2508
2517
 
2509
2518
  @override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2510
2519
  def test_get_run_with_constrained_permission(self):
@@ -2603,12 +2612,12 @@ class JobTestCase(
2603
2612
  self.add_permissions("extras.run_job")
2604
2613
 
2605
2614
  for run_url in (
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}),
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}),
2608
2617
  ):
2609
2618
  response = self.client.post(run_url, self.data_run_immediately)
2610
2619
  self.assertBodyContains(response, "Job is not enabled to be run")
2611
- self.assertFalse(JobResult.objects.filter(name="fail.TestFailJob").exists())
2620
+ self.assertFalse(JobResult.objects.filter(name="fail.TestFail").exists())
2612
2621
 
2613
2622
  def test_run_now_missing_args(self):
2614
2623
  self.add_permissions("extras.run_job")
@@ -2644,16 +2653,10 @@ class JobTestCase(
2644
2653
  job_celery_kwargs = {
2645
2654
  "nautobot_job_job_model_id": self.test_required_args.id,
2646
2655
  "nautobot_job_profile": True,
2647
- "nautobot_job_ignore_singleton_lock": True,
2648
2656
  "nautobot_job_user_id": self.user.id,
2649
2657
  "queue": job_queue.name,
2650
2658
  }
2651
2659
  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()
2657
2660
  previous_result = JobResult.objects.create(
2658
2661
  job_model=self.test_required_args,
2659
2662
  user=self.user,
@@ -2670,9 +2673,6 @@ class JobTestCase(
2670
2673
  content,
2671
2674
  )
2672
2675
  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,6 +2844,59 @@ 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
+
2847
2900
 
2848
2901
  class JobButtonTestCase(
2849
2902
  ViewTestCases.CreateObjectViewTestCase,
@@ -3226,6 +3279,94 @@ class RelationshipTestCase(
3226
3279
 
3227
3280
  cls.slug_test_object = "Primary Interface"
3228
3281
 
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
+
3229
3370
 
3230
3371
  class RelationshipAssociationTestCase(
3231
3372
  # TODO? ViewTestCases.CreateObjectViewTestCase,
@@ -3608,8 +3749,6 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
3608
3749
 
3609
3750
  cls.bulk_edit_data = {
3610
3751
  "color": "000000",
3611
- "description": "I used to be a new role object.",
3612
- "weight": 255,
3613
3752
  }
3614
3753
 
3615
3754
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
nautobot/extras/utils.py CHANGED
@@ -579,7 +579,6 @@ 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,
583
582
  },
584
583
  )
585
584
 
@@ -666,9 +665,9 @@ def run_kubernetes_job_and_return_job_result(job_queue, job_result, job_kwargs):
666
665
  "runjob_with_job_result",
667
666
  f"{job_result.pk}",
668
667
  ]
669
- job_result.log(f"Creating job pod {pod_name} in namespace {pod_namespace}")
668
+ logger.info("Creating job pod %s in namespace %s", pod_name, pod_namespace)
670
669
  api_instance.create_namespaced_job(body=pod_manifest, namespace=pod_namespace)
671
- job_result.log(f"Reading job pod {pod_name} in namespace {pod_namespace}")
670
+ logger.info("Reading job pod %s in namespace %s", pod_name, pod_namespace)
672
671
  api_instance.read_namespaced_job(name="nautobot-job-" + str(job_result.pk), namespace=pod_namespace)
673
672
  return job_result
674
673