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
@@ -43,7 +43,15 @@
43
43
  <td>Units</td>
44
44
  <td>{{ object.unit_list }}</td>
45
45
  </tr>
46
- {% include 'inc/tenant_table_row.html' %}
46
+ <tr>
47
+ <td>Tenant</td>
48
+ <td>
49
+ {% if object.tenant and object.tenant.tenant_group %}
50
+ {{ object.tenant.tenant_group|hyperlinked_object }} /
51
+ {% endif %}
52
+ {{ object.tenant|hyperlinked_object }}
53
+ </td>
54
+ </tr>
47
55
  <tr>
48
56
  <td>User</td>
49
57
  <td>{{ object.user }}</td>
@@ -54,7 +54,15 @@
54
54
  {{ object.primary_ip6|hyperlinked_object }}
55
55
  </td>
56
56
  </tr>
57
- {% include 'inc/tenant_table_row.html' %}
57
+ <tr>
58
+ <td>Tenant</td>
59
+ <td>
60
+ {% if object.tenant and object.tenant.tenant_group %}
61
+ {{ object.tenant.tenant_group|hyperlinked_object }} /
62
+ {% endif %}
63
+ {{ object.tenant|hyperlinked_object }}
64
+ </td>
65
+ </tr>
58
66
  <tr>
59
67
  <td>Description</td>
60
68
  <td>{{ object.description|placeholder }}</td>
@@ -23,6 +23,12 @@
23
23
  {% endif %}
24
24
  </div>
25
25
  </div>
26
- {% include 'inc/tenancy_form_panel.html' %}
26
+ <div class="panel panel-default">
27
+ <div class="panel-heading"><strong>Tenancy</strong></div>
28
+ <div class="panel-body">
29
+ {% render_field form.tenant_group %}
30
+ {% render_field form.tenant %}
31
+ </div>
32
+ </div>
27
33
  {% include 'inc/extras_features_edit_form_fields.html' %}
28
34
  {% endblock %}
@@ -1469,24 +1469,11 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1469
1469
  SecretsGroup.objects.create(name="Secrets Group 2"),
1470
1470
  )
1471
1471
 
1472
- device_type = DeviceType.objects.first()
1472
+ device_type = DeviceType.objects.filter(software_image_files__isnull=False).first()
1473
1473
  device_role = Role.objects.get_for_model(Device).first()
1474
1474
 
1475
- software_version = SoftwareVersion.objects.first()
1476
- software_image_files = (
1477
- SoftwareImageFile.objects.create(
1478
- status=Status.objects.get_for_model(SoftwareImageFile).first(),
1479
- software_version=software_version,
1480
- image_file_name="test_software_image_file_1.bin",
1481
- ),
1482
- SoftwareImageFile.objects.create(
1483
- status=Status.objects.get_for_model(SoftwareImageFile).last(),
1484
- software_version=software_version,
1485
- image_file_name="test_software_image_file_2.bin",
1486
- ),
1487
- )
1488
- for software_image_file in software_image_files:
1489
- software_image_file.device_types.add(device_type)
1475
+ software_version = SoftwareVersion.objects.filter(software_image_files__device_types=device_type).first()
1476
+ software_image_files = SoftwareImageFile.objects.exclude(software_version=software_version)[:2]
1490
1477
 
1491
1478
  Device.objects.create(
1492
1479
  device_type=device_type,
@@ -1,4 +1,3 @@
1
- # pylint: disable=no-member # it doesn't recognize the class attributes assigned in common_test_data()
2
1
  import uuid
3
2
 
4
3
  from django.contrib.auth import get_user_model
@@ -1692,7 +1691,6 @@ class DeviceTestCase(
1692
1691
  ("front_ports", "front_ports__id"),
1693
1692
  ("interfaces", "interfaces__id"),
1694
1693
  ("interfaces", "interfaces__name"),
1695
- ("ip_addresses", "interfaces__ip_addresses__id"),
1696
1694
  ("mac_address", "interfaces__mac_address"),
1697
1695
  ("manufacturer", "device_type__manufacturer__id"),
1698
1696
  ("manufacturer", "device_type__manufacturer__name"),
@@ -1878,14 +1876,6 @@ class DeviceTestCase(
1878
1876
  Device.objects.filter(primary_ip4__isnull=True, primary_ip6__isnull=True),
1879
1877
  )
1880
1878
 
1881
- def test_ip_addresses(self):
1882
- addresses = list(IPAddress.objects.filter(interfaces__isnull=False)[:2])
1883
- params = {"ip_addresses": [addresses[0].address, addresses[1].id]}
1884
- self.assertQuerysetEqualAndNotEmpty(
1885
- self.filterset(params, self.queryset).qs,
1886
- self.queryset.filter(interfaces__ip_addresses__in=addresses).distinct(),
1887
- )
1888
-
1889
1879
  def test_virtual_chassis_member(self):
1890
1880
  # TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
1891
1881
  with self.subTest():
@@ -2141,7 +2131,6 @@ class InterfaceTestCase(PathEndpointModelTestMixin, ModularDeviceComponentTestMi
2141
2131
  ("child_interfaces", "child_interfaces__name"),
2142
2132
  ("description",),
2143
2133
  # ("device", "device__id"), # TODO - InterfaceFilterSet overrides device as a MultiValueCharFilter on name only
2144
- ("ip_addresses", "ip_addresses__id"),
2145
2134
  ("label",),
2146
2135
  ("lag", "lag__id"),
2147
2136
  ("lag", "lag__name"),
@@ -2389,20 +2378,6 @@ class InterfaceTestCase(PathEndpointModelTestMixin, ModularDeviceComponentTestMi
2389
2378
  status=interface_statuses[3],
2390
2379
  )
2391
2380
 
2392
- ipaddr_status = Status.objects.get_for_model(IPAddress).first()
2393
- prefix_status = Status.objects.get_for_model(Prefix).first()
2394
- namespace = Namespace.objects.first()
2395
- Prefix.objects.create(prefix="192.0.2.0/24", namespace=namespace, status=prefix_status)
2396
- Prefix.objects.create(prefix="2600::/64", namespace=namespace, status=prefix_status)
2397
- ipaddresses = (
2398
- IPAddress.objects.create(address="192.0.2.1/24", namespace=namespace, status=ipaddr_status),
2399
- IPAddress.objects.create(address="192.0.2.2/24", namespace=namespace, status=ipaddr_status),
2400
- IPAddress.objects.create(address="2600::1/120", namespace=namespace, status=ipaddr_status),
2401
- IPAddress.objects.create(address="2600::0100/120", namespace=namespace, status=ipaddr_status),
2402
- )
2403
-
2404
- cabled_interfaces[0].add_ip_addresses([ipaddresses[0], ipaddresses[2]])
2405
- cabled_interfaces[1].add_ip_addresses([ipaddresses[1], ipaddresses[3]])
2406
2381
  # Virtual Device Context
2407
2382
  vdc_status = Status.objects.get_for_model(VirtualDeviceContext).first()
2408
2383
  vdcs = [
@@ -2648,14 +2623,6 @@ class InterfaceTestCase(PathEndpointModelTestMixin, ModularDeviceComponentTestMi
2648
2623
  with self.subTest("device (pk) filter with an invalid uuid"):
2649
2624
  self.assertFalse(self.filterset({"device": [uuid.uuid4()]}, self.queryset).is_valid())
2650
2625
 
2651
- def test_ip_addresses(self):
2652
- addresses = list(IPAddress.objects.filter(interfaces__isnull=False)[:2])
2653
- params = {"ip_addresses": [addresses[0].address, addresses[1].id]}
2654
- self.assertQuerysetEqualAndNotEmpty(
2655
- self.filterset(params, self.queryset).qs,
2656
- self.queryset.filter(ip_addresses__in=addresses).distinct(),
2657
- )
2658
-
2659
2626
  def test_kind(self):
2660
2627
  # TODO: Not a generic_filter_test because this is a single-value filter
2661
2628
  # 2.0 TODO: Support filtering for multiple values
@@ -1,16 +1,8 @@
1
1
  from django.test import TestCase
2
2
 
3
3
  from nautobot.core.testing.forms import FormTestCases
4
- from nautobot.core.testing.mixins import NautobotTestCaseMixin
5
4
  from nautobot.dcim.choices import DeviceFaceChoices, InterfaceModeChoices, InterfaceTypeChoices, RackWidthChoices
6
- from nautobot.dcim.forms import (
7
- DeviceFilterForm,
8
- DeviceForm,
9
- InterfaceBulkEditForm,
10
- InterfaceCreateForm,
11
- InterfaceForm,
12
- RackForm,
13
- )
5
+ from nautobot.dcim.forms import DeviceFilterForm, DeviceForm, InterfaceCreateForm, InterfaceForm, RackForm
14
6
  from nautobot.dcim.models import (
15
7
  Device,
16
8
  DeviceType,
@@ -328,7 +320,7 @@ class RackTestCase(TestCase):
328
320
  self.assertTrue(form.is_valid())
329
321
 
330
322
 
331
- class InterfaceTestCase(NautobotTestCaseMixin, TestCase):
323
+ class InterfaceTestCase(TestCase):
332
324
  @classmethod
333
325
  def setUpTestData(cls):
334
326
  cls.device = Device.objects.first()
@@ -388,44 +380,3 @@ class InterfaceTestCase(NautobotTestCaseMixin, TestCase):
388
380
  self.vlan.locations.clear()
389
381
  form = InterfaceForm(data=self.data, instance=self.interface)
390
382
  self.assertTrue(form.is_valid())
391
-
392
- def test_untagged_vlans_dropdown_options_align_in_interface_edit_form_and_bulk_edit_form(self):
393
- """
394
- Assert that untagged_vlans field dropdown are populated correctly in InterfaceForm and InterfaceBulkEditForm,
395
- and that the queryset is the same for both forms.
396
- """
397
- status = Status.objects.get_for_model(Interface).first()
398
- location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
399
- devices = Device.objects.all()[:3]
400
- for device in devices:
401
- device.location = location
402
- device.save()
403
- interfaces = (
404
- Interface.objects.create(
405
- device=devices[0],
406
- name="Test Interface 1",
407
- type=InterfaceTypeChoices.TYPE_2GFC_SFP,
408
- status=status,
409
- ),
410
- Interface.objects.create(
411
- device=devices[1],
412
- name="Test Interface 2",
413
- type=InterfaceTypeChoices.TYPE_LAG,
414
- status=status,
415
- ),
416
- Interface.objects.create(
417
- device=devices[2],
418
- name="Test Interface 3",
419
- type=InterfaceTypeChoices.TYPE_100ME_FIXED,
420
- status=status,
421
- ),
422
- )
423
- edit_form = InterfaceForm(data=self.data, instance=interfaces[0])
424
- bulk_edit_form = InterfaceBulkEditForm(
425
- model=Interface,
426
- data={"pks": [interface.pk for interface in interfaces]},
427
- )
428
- self.assertQuerysetEqualAndNotEmpty(
429
- edit_form.fields["untagged_vlan"].queryset,
430
- bulk_edit_form.fields["untagged_vlan"].queryset,
431
- )
@@ -83,55 +83,3 @@ class GraphQLTestCase(TestCase):
83
83
  self.assertEqual(
84
84
  resp["data"]["devices"][0]["platform"]["network_driver_mappings"]["scrapli"], "cisco_iosxe"
85
85
  )
86
-
87
- with self.subTest("device serial number query"):
88
- non_empty_serial_device = Device.objects.first()
89
- non_empty_serial_device.serial = "1234567890abceFGHIJKL"
90
- non_empty_serial_device.save()
91
-
92
- # Test device serial query default behavior: serial__ie
93
- query = 'query { devices (serial: " ") { name serial } }'
94
- resp = execute_query(query, user=self.user).to_dict()
95
- self.assertFalse(resp["data"].get("error"))
96
- for device in resp["data"]["devices"]:
97
- self.assertEqual(device["serial"], "")
98
-
99
- # Test device serial default filter with non-empty serial number
100
- query = 'query { devices (serial:"' + non_empty_serial_device.serial.lower() + '") { name serial } }'
101
- resp = execute_query(query, user=self.user).to_dict()
102
- self.assertFalse(resp["data"].get("error"))
103
- self.assertEqual(resp["data"]["devices"][0]["serial"], non_empty_serial_device.serial)
104
-
105
- # Test device serial iexact filter with non-empty serial number
106
- query = 'query { devices (serial__ie:"' + non_empty_serial_device.serial.upper() + '") { name serial } }'
107
- resp = execute_query(query, user=self.user).to_dict()
108
- self.assertFalse(resp["data"].get("error"))
109
- self.assertEqual(resp["data"]["devices"][0]["serial"], non_empty_serial_device.serial)
110
-
111
- # Test device serial__nie filter with non-empty serial number
112
- query = 'query { devices (serial__nie:"' + non_empty_serial_device.serial.lower() + '") { name serial } }'
113
- resp = execute_query(query, user=self.user).to_dict()
114
- self.assertFalse(resp["data"].get("error"))
115
- for device in resp["data"]["devices"]:
116
- self.assertNotEqual(device["serial"], non_empty_serial_device.serial)
117
-
118
- # Test device serial__ie filter with empty serial number
119
- query = 'query { devices (serial__ie:" ") { name serial } }'
120
- resp = execute_query(query, user=self.user).to_dict()
121
- self.assertFalse(resp["data"].get("error"))
122
- for device in resp["data"]["devices"]:
123
- self.assertEqual(device["serial"], "")
124
-
125
- # Test device serial__nie filter with empty serial number
126
- query = 'query { devices (serial__nie:" ") { name serial } }'
127
- resp = execute_query(query, user=self.user).to_dict()
128
- self.assertFalse(resp["data"].get("error"))
129
- for device in resp["data"]["devices"]:
130
- self.assertNotEqual(device["serial"], "")
131
-
132
- # Test device serial__n filter with empty serial number
133
- query = 'query { devices (serial__n:" ") { name serial } }'
134
- resp = execute_query(query, user=self.user).to_dict()
135
- self.assertFalse(resp["data"].get("error"))
136
- for device in resp["data"]["devices"]:
137
- self.assertNotEqual(device["serial"], "")
@@ -4,7 +4,6 @@ from constance.test import override_config
4
4
  from django.contrib.contenttypes.models import ContentType
5
5
  from django.core.exceptions import ValidationError
6
6
  from django.db import IntegrityError
7
- from django.db.models import Model
8
7
  from django.test import TestCase
9
8
  from django.test.utils import override_settings
10
9
 
@@ -78,7 +77,7 @@ class ModularDeviceComponentTestCaseMixin:
78
77
 
79
78
  # fields required to create instances of the model, with the exception of name, device_field and module_field
80
79
  modular_component_create_data = {}
81
- model: type[Model]
80
+ model = None
82
81
  device_field = "device" # field name for the parent device
83
82
  module_field = "module" # field name for the parent module
84
83
 
@@ -1955,8 +1954,6 @@ class CableTestCase(ModelTestCases.BaseModelTestCase):
1955
1954
 
1956
1955
  @classmethod
1957
1956
  def setUpTestData(cls):
1958
- cls.interface_choices = {section[0]: dict(section[1]) for section in InterfaceTypeChoices.CHOICES}
1959
-
1960
1957
  location = Location.objects.first()
1961
1958
  manufacturer = Manufacturer.objects.first()
1962
1959
  devicetype = DeviceType.objects.create(
@@ -2185,46 +2182,20 @@ class CableTestCase(ModelTestCases.BaseModelTestCase):
2185
2182
  """
2186
2183
  A cable cannot terminate to a virtual interface
2187
2184
  """
2188
- self.cable.delete()
2189
- interface_status = Status.objects.get_for_model(Interface).first()
2190
- virtual_interface = Interface.objects.create(
2191
- device=self.device1,
2192
- name="V1",
2193
- type=InterfaceTypeChoices.TYPE_VIRTUAL,
2194
- status=interface_status,
2195
- )
2185
+ virtual_interface = Interface(device=self.device1, name="V1", type=InterfaceTypeChoices.TYPE_VIRTUAL)
2196
2186
  cable = Cable(termination_a=self.interface2, termination_b=virtual_interface)
2197
- with self.assertRaises(ValidationError) as cm:
2187
+ with self.assertRaises(ValidationError):
2198
2188
  cable.clean()
2199
2189
 
2200
- virtual_interface_choices = self.interface_choices["Virtual interfaces"]
2201
- self.assertIn(
2202
- f"Cables cannot be terminated to {virtual_interface_choices[InterfaceTypeChoices.TYPE_VIRTUAL]} interfaces",
2203
- str(cm.exception),
2204
- )
2205
-
2206
2190
  def test_cable_cannot_terminate_to_a_wireless_interface(self):
2207
2191
  """
2208
2192
  A cable cannot terminate to a wireless interface
2209
2193
  """
2210
- self.cable.delete()
2211
- interface_status = Status.objects.get_for_model(Interface).first()
2212
- wireless_interface = Interface.objects.create(
2213
- device=self.device1,
2214
- name="W1",
2215
- type=InterfaceTypeChoices.TYPE_80211A,
2216
- status=interface_status,
2217
- )
2194
+ wireless_interface = Interface(device=self.device1, name="W1", type=InterfaceTypeChoices.TYPE_80211A)
2218
2195
  cable = Cable(termination_a=self.interface2, termination_b=wireless_interface)
2219
- with self.assertRaises(ValidationError) as cm:
2196
+ with self.assertRaises(ValidationError):
2220
2197
  cable.clean()
2221
2198
 
2222
- wireless_interface_choices = self.interface_choices["Wireless"]
2223
- self.assertIn(
2224
- f"Cables cannot be terminated to {wireless_interface_choices[InterfaceTypeChoices.TYPE_80211A]} interfaces",
2225
- str(cm.exception),
2226
- )
2227
-
2228
2199
  def test_create_cable_with_missing_status_connected(self):
2229
2200
  """Test for https://github.com/nautobot/nautobot/issues/2081"""
2230
2201
  # Delete all cables because some cables has connected status.
@@ -116,7 +116,6 @@ from nautobot.extras.models import (
116
116
  CustomField,
117
117
  CustomFieldChoice,
118
118
  ExternalIntegration,
119
- JobResult,
120
119
  Relationship,
121
120
  RelationshipAssociation,
122
121
  Role,
@@ -129,7 +128,6 @@ from nautobot.ipam.choices import IPAddressTypeChoices
129
128
  from nautobot.ipam.models import IPAddress, Namespace, Prefix, VLAN, VLANGroup, VRF
130
129
  from nautobot.tenancy.models import Tenant
131
130
  from nautobot.users.models import ObjectPermission
132
- from nautobot.virtualization.models import Cluster, ClusterType
133
131
 
134
132
  # Use the proper swappable User model
135
133
  User = get_user_model()
@@ -1152,15 +1150,9 @@ module-bays:
1152
1150
  }
1153
1151
 
1154
1152
  response = self.client.post(url, data)
1155
- job_result = JobResult.objects.filter(name="Bulk Edit Objects").first()
1156
- # Assert successfull redirect to Job Results; whcih means no form validation error was raised
1157
- self.assertRedirects(
1158
- response,
1159
- reverse("extras:jobresult", args=[job_result.pk]),
1160
- status_code=302,
1161
- target_status_code=200,
1162
- )
1163
1153
  self.assertHttpStatus(response, 302)
1154
+ for instance in self._get_queryset().filter(pk__in=pk_list):
1155
+ self.assertEqual(instance.u_height, data["u_height"])
1164
1156
 
1165
1157
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
1166
1158
  def test_rack_height_bulk_edit_invalid(self):
@@ -1176,12 +1168,7 @@ module-bays:
1176
1168
  }
1177
1169
 
1178
1170
  response = self.client.post(url, data)
1179
- response_content = response.content.decode(response.charset)
1180
- self.assertHttpStatus(response, 200)
1181
- self.assertInHTML(
1182
- '<strong class="panel-title">U height</strong>: <ul class="errorlist"><li>Ensure this value is greater than or equal to 0.</li></ul>',
1183
- response_content,
1184
- )
1171
+ self.assertBodyContains(response, "failed validation")
1185
1172
 
1186
1173
 
1187
1174
  class ModuleTypeTestCase(
@@ -2058,9 +2045,6 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2058
2045
 
2059
2046
  rack_group = RackGroup.objects.create(location=locations[0], name="Rack Group 1")
2060
2047
 
2061
- cluster_type = ClusterType.objects.create(name="Cluster Type 1")
2062
- cluster = Cluster.objects.create(name="Cluster 1", cluster_type=cluster_type)
2063
-
2064
2048
  rack_status = Status.objects.get_for_model(Rack).first()
2065
2049
  racks = (
2066
2050
  Rack.objects.create(
@@ -2232,8 +2216,6 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2232
2216
  "status": statuses[2].pk,
2233
2217
  "location": locations[1].pk,
2234
2218
  "rack": racks[1].pk,
2235
- "cluster": cluster.pk,
2236
- "comments": "An older device",
2237
2219
  "position": None,
2238
2220
  "face": DeviceFaceChoices.FACE_FRONT,
2239
2221
  "secrets_group": secrets_groups[1].pk,
@@ -4592,6 +4574,46 @@ class SoftwareImageFileTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4592
4574
  "download_url": "https://example.com/software_image_file_test_case.bin",
4593
4575
  }
4594
4576
 
4577
+ @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
4578
+ def test_correct_handling_for_model_protected_error(self):
4579
+ platform = Platform.objects.first()
4580
+ software_version_status = Status.objects.get_for_model(SoftwareVersion).first()
4581
+ software_image_file_status = Status.objects.get_for_model(SoftwareImageFile).first()
4582
+ software_version = SoftwareVersion.objects.create(
4583
+ platform=platform, version="Test version 1.0.0", status=software_version_status
4584
+ )
4585
+ software_image_file = SoftwareImageFile.objects.create(
4586
+ software_version=software_version,
4587
+ image_file_name="software_image_file_qs_test_1.bin",
4588
+ status=software_image_file_status,
4589
+ )
4590
+ device_type = DeviceType.objects.first()
4591
+ device_role = Role.objects.get_for_model(Device).first()
4592
+ device_status = Status.objects.get_for_model(Device).first()
4593
+ location = Location.objects.filter(location_type__name="Campus").first()
4594
+ Device.objects.create(
4595
+ device_type=device_type,
4596
+ role=device_role,
4597
+ name="Device 1",
4598
+ location=location,
4599
+ status=device_status,
4600
+ software_version=software_version,
4601
+ )
4602
+ device_type_to_software_image_file = DeviceTypeToSoftwareImageFile.objects.create(
4603
+ device_type=device_type, software_image_file=software_image_file
4604
+ )
4605
+
4606
+ self.add_permissions("dcim.delete_softwareimagefile")
4607
+ pk_list = [software_image_file.pk]
4608
+ data = {
4609
+ "pk": pk_list,
4610
+ "confirm": True,
4611
+ "_confirm": True, # Form button
4612
+ }
4613
+ response = self.client.post(self._get_url("bulk_delete"), data, follow=True)
4614
+ # Assert protected error message included in the response body
4615
+ self.assertBodyContains(response, f"<span>{device_type_to_software_image_file}</span>", html=True)
4616
+
4595
4617
 
4596
4618
  class SoftwareVersionTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4597
4619
  model = SoftwareVersion
@@ -4630,6 +4652,46 @@ class SoftwareVersionTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4630
4652
  "pre_release": True,
4631
4653
  }
4632
4654
 
4655
+ @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
4656
+ def test_correct_handling_for_model_protected_error(self):
4657
+ platform = Platform.objects.first()
4658
+ software_version_status = Status.objects.get_for_model(SoftwareVersion).first()
4659
+ software_image_file_status = Status.objects.get_for_model(SoftwareImageFile).first()
4660
+ software_version = SoftwareVersion.objects.create(
4661
+ platform=platform, version="Test version 1.0.0", status=software_version_status
4662
+ )
4663
+ software_image_file = SoftwareImageFile.objects.create(
4664
+ software_version=software_version,
4665
+ image_file_name="software_image_file_qs_test_1.bin",
4666
+ status=software_image_file_status,
4667
+ )
4668
+ device_type = DeviceType.objects.first()
4669
+ device_role = Role.objects.get_for_model(Device).first()
4670
+ device_status = Status.objects.get_for_model(Device).first()
4671
+ location = Location.objects.filter(location_type__name="Campus").first()
4672
+ Device.objects.create(
4673
+ device_type=device_type,
4674
+ role=device_role,
4675
+ name="Device 1",
4676
+ location=location,
4677
+ status=device_status,
4678
+ software_version=software_version,
4679
+ )
4680
+ device_type_to_software_image_file = DeviceTypeToSoftwareImageFile.objects.create(
4681
+ device_type=device_type, software_image_file=software_image_file
4682
+ )
4683
+
4684
+ self.add_permissions("dcim.delete_softwareversion")
4685
+ pk_list = [software_version.pk]
4686
+ data = {
4687
+ "pk": pk_list,
4688
+ "confirm": True,
4689
+ "_confirm": True, # Form button
4690
+ }
4691
+ response = self.client.post(self._get_url("bulk_delete"), data, follow=True)
4692
+ # Assert protected error message included in the response body
4693
+ self.assertBodyContains(response, f"<span>{device_type_to_software_image_file}</span>", html=True)
4694
+
4633
4695
 
4634
4696
  class ControllerTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4635
4697
  model = Controller
nautobot/dcim/views.py CHANGED
@@ -697,7 +697,7 @@ class RackReservationEditView(generic.ObjectEditView):
697
697
  model_form = forms.RackReservationForm
698
698
  template_name = "dcim/rackreservation_edit.html"
699
699
 
700
- def alter_obj(self, obj, request, url_args, url_kwargs):
700
+ def alter_obj(self, obj, request, args, kwargs):
701
701
  if not obj.present_in_database:
702
702
  if "rack" in request.GET:
703
703
  obj.rack = get_object_or_404(Rack, pk=request.GET.get("rack"))
@@ -4502,6 +4502,12 @@ class ControllerManagedDeviceGroupUIViewSet(NautobotUIViewSet):
4502
4502
 
4503
4503
  return obj
4504
4504
 
4505
+ def extra_post_save_action(self, obj, form):
4506
+ if form.cleaned_data.get("add_radio_profiles", None):
4507
+ obj.radio_profiles.add(*form.cleaned_data["add_radio_profiles"])
4508
+ if form.cleaned_data.get("remove_radio_profiles", None):
4509
+ obj.radio_profiles.remove(*form.cleaned_data["remove_radio_profiles"])
4510
+
4505
4511
 
4506
4512
  #
4507
4513
  # Virtual Device Context
@@ -4528,3 +4534,9 @@ class VirtualDeviceContextUIViewSet(NautobotUIViewSet):
4528
4534
  **super().get_extra_context(request, instance),
4529
4535
  }
4530
4536
  return super().get_extra_context(request, instance)
4537
+
4538
+ def extra_post_save_action(self, obj, form):
4539
+ if form.cleaned_data.get("add_interfaces", None):
4540
+ obj.prefixes.add(*form.cleaned_data["add_interfaces"])
4541
+ if form.cleaned_data.get("remove_interfaces", None):
4542
+ obj.prefixes.remove(*form.cleaned_data["remove_interfaces"])
@@ -42,8 +42,8 @@ class CustomFieldsDataField(Field):
42
42
  def custom_field_keys(self):
43
43
  return CustomField.objects.keys_for_model(self.parent.Meta.model)
44
44
 
45
- def to_representation(self, value):
46
- return {key: value.get(key) for key in self.custom_field_keys}
45
+ def to_representation(self, obj):
46
+ return {key: obj.get(key) for key in self.custom_field_keys}
47
47
 
48
48
  def to_internal_value(self, data):
49
49
  """Support updates to individual fields on an existing instance without needing to provide the entire dict."""