nautobot 2.3.2__py3-none-any.whl → 2.3.4__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 (326) hide show
  1. nautobot/core/celery/schedulers.py +2 -2
  2. nautobot/core/settings.py +3 -1
  3. nautobot/core/settings.yaml +40 -23
  4. nautobot/core/tests/runner.py +27 -9
  5. nautobot/core/tests/test_settings_schema.py +7 -3
  6. nautobot/core/tests/test_utils.py +13 -0
  7. nautobot/core/utils/lookup.py +7 -1
  8. nautobot/dcim/tables/power.py +1 -1
  9. nautobot/dcim/tests/test_filters.py +1 -1
  10. nautobot/extras/context_managers.py +11 -4
  11. nautobot/extras/jobs.py +0 -1
  12. nautobot/extras/models/groups.py +4 -1
  13. nautobot/extras/tests/test_context_managers.py +33 -14
  14. nautobot/extras/tests/test_dynamicgroups.py +11 -0
  15. nautobot/extras/tests/test_models.py +5 -0
  16. nautobot/extras/tests/test_views.py +1 -0
  17. nautobot/ipam/models.py +4 -0
  18. nautobot/ipam/tests/test_api.py +11 -0
  19. nautobot/project-static/docs/404.html +81 -169
  20. nautobot/project-static/docs/additional-features/caching.html +3 -3
  21. nautobot/project-static/docs/additional-features/healthcheck.html +3 -3
  22. nautobot/project-static/docs/apps/index.html +83 -171
  23. nautobot/project-static/docs/apps/nautobot-apps.html +82 -170
  24. nautobot/project-static/docs/assets/javascripts/bundle.56dfad97.min.js +16 -0
  25. nautobot/project-static/docs/assets/javascripts/bundle.56dfad97.min.js.map +7 -0
  26. nautobot/project-static/docs/assets/javascripts/workers/{search.b8dbb3d2.min.js → search.07f07601.min.js} +1 -1
  27. nautobot/project-static/docs/assets/javascripts/workers/{search.b8dbb3d2.min.js.map → search.07f07601.min.js.map} +1 -1
  28. nautobot/project-static/docs/assets/stylesheets/main.35f28582.min.css +1 -0
  29. nautobot/project-static/docs/assets/stylesheets/main.35f28582.min.css.map +1 -0
  30. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +84 -172
  31. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +84 -172
  32. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +116 -204
  33. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +90 -177
  34. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +89 -177
  35. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +83 -171
  36. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +83 -171
  37. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +88 -176
  38. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +92 -180
  39. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +99 -187
  40. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +106 -194
  41. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +150 -238
  42. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +99 -187
  43. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +143 -231
  44. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +142 -230
  45. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +84 -172
  46. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +86 -174
  47. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +98 -186
  48. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +136 -224
  49. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +135 -223
  50. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +84 -172
  51. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +138 -226
  52. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +125 -213
  53. nautobot/project-static/docs/configuration/optional-settings.html +3 -3
  54. nautobot/project-static/docs/configuration/required-settings.html +3 -3
  55. nautobot/project-static/docs/development/apps/api/configuration-view.html +83 -171
  56. nautobot/project-static/docs/development/apps/api/database-backend-config.html +83 -171
  57. nautobot/project-static/docs/development/apps/api/models/django-admin.html +83 -171
  58. nautobot/project-static/docs/development/apps/api/models/global-search.html +83 -171
  59. nautobot/project-static/docs/development/apps/api/models/graphql.html +83 -171
  60. nautobot/project-static/docs/development/apps/api/models/index.html +83 -171
  61. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +83 -171
  62. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +83 -171
  63. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +83 -171
  64. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +83 -171
  65. nautobot/project-static/docs/development/apps/api/platform-features/index.html +83 -171
  66. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +83 -171
  67. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +83 -171
  68. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +83 -171
  69. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +83 -171
  70. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +85 -173
  71. nautobot/project-static/docs/development/apps/api/prometheus.html +83 -171
  72. nautobot/project-static/docs/development/apps/api/setup.html +83 -171
  73. nautobot/project-static/docs/development/apps/api/testing.html +84 -172
  74. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +83 -171
  75. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +83 -171
  76. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +83 -171
  77. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +83 -171
  78. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +83 -171
  79. nautobot/project-static/docs/development/apps/api/views/base-template.html +83 -171
  80. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +83 -171
  81. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +83 -171
  82. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +83 -171
  83. nautobot/project-static/docs/development/apps/api/views/index.html +83 -171
  84. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +83 -171
  85. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +83 -171
  86. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +83 -171
  87. nautobot/project-static/docs/development/apps/api/views/notes.html +83 -171
  88. nautobot/project-static/docs/development/apps/api/views/rest-api.html +83 -171
  89. nautobot/project-static/docs/development/apps/api/views/urls.html +83 -171
  90. nautobot/project-static/docs/development/apps/index.html +84 -172
  91. nautobot/project-static/docs/development/apps/migration/code-updates.html +83 -171
  92. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +83 -171
  93. nautobot/project-static/docs/development/apps/migration/from-v1.html +83 -171
  94. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +83 -171
  95. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +83 -171
  96. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +83 -171
  97. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +83 -171
  98. nautobot/project-static/docs/development/apps/porting-from-netbox.html +83 -171
  99. nautobot/project-static/docs/development/core/application-registry.html +83 -171
  100. nautobot/project-static/docs/development/core/best-practices.html +83 -171
  101. nautobot/project-static/docs/development/core/bootstrap-ui.html +83 -171
  102. nautobot/project-static/docs/development/core/caching.html +83 -171
  103. nautobot/project-static/docs/development/core/controllers.html +83 -171
  104. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +83 -171
  105. nautobot/project-static/docs/development/core/generic-views.html +83 -171
  106. nautobot/project-static/docs/development/core/getting-started.html +106 -191
  107. nautobot/project-static/docs/development/core/homepage.html +83 -171
  108. nautobot/project-static/docs/development/core/index.html +83 -171
  109. nautobot/project-static/docs/development/core/model-checklist.html +83 -171
  110. nautobot/project-static/docs/development/core/model-features.html +83 -171
  111. nautobot/project-static/docs/development/core/natural-keys.html +83 -171
  112. nautobot/project-static/docs/development/core/navigation-menu.html +83 -171
  113. nautobot/project-static/docs/development/core/release-checklist.html +83 -171
  114. nautobot/project-static/docs/development/core/role-internals.html +83 -171
  115. nautobot/project-static/docs/development/core/settings.html +83 -171
  116. nautobot/project-static/docs/development/core/style-guide.html +83 -171
  117. nautobot/project-static/docs/development/core/templates.html +83 -171
  118. nautobot/project-static/docs/development/core/testing.html +85 -173
  119. nautobot/project-static/docs/development/core/user-preferences.html +83 -171
  120. nautobot/project-static/docs/development/index.html +83 -171
  121. nautobot/project-static/docs/development/jobs/index.html +91 -179
  122. nautobot/project-static/docs/development/jobs/migration/from-v1.html +84 -172
  123. nautobot/project-static/docs/docker/index.html +3 -3
  124. nautobot/project-static/docs/index.html +88 -176
  125. nautobot/project-static/docs/installation/selinux-troubleshooting.html +3 -3
  126. nautobot/project-static/docs/overview/application_stack.html +89 -177
  127. nautobot/project-static/docs/overview/design_philosophy.html +83 -171
  128. nautobot/project-static/docs/release-notes/index.html +83 -171
  129. nautobot/project-static/docs/release-notes/version-1.0.html +83 -171
  130. nautobot/project-static/docs/release-notes/version-1.1.html +84 -172
  131. nautobot/project-static/docs/release-notes/version-1.2.html +86 -174
  132. nautobot/project-static/docs/release-notes/version-1.3.html +84 -172
  133. nautobot/project-static/docs/release-notes/version-1.4.html +85 -173
  134. nautobot/project-static/docs/release-notes/version-1.5.html +85 -173
  135. nautobot/project-static/docs/release-notes/version-1.6.html +87 -175
  136. nautobot/project-static/docs/release-notes/version-2.0.html +84 -172
  137. nautobot/project-static/docs/release-notes/version-2.1.html +85 -173
  138. nautobot/project-static/docs/release-notes/version-2.2.html +83 -171
  139. nautobot/project-static/docs/release-notes/version-2.3.html +425 -235
  140. nautobot/project-static/docs/search/search_index.json +1 -1
  141. nautobot/project-static/docs/sitemap.xml +277 -556
  142. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  143. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +85 -173
  144. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +84 -172
  145. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +85 -173
  146. nautobot/project-static/docs/user-guide/administration/configuration/index.html +94 -192
  147. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +13 -12524
  148. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +8966 -0
  149. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +13 -9218
  150. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +12734 -0
  151. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +87 -175
  152. nautobot/project-static/docs/user-guide/administration/guides/caching.html +13 -9108
  153. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +84 -172
  154. nautobot/project-static/docs/user-guide/administration/guides/docker.html +9491 -0
  155. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +9478 -0
  156. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +13 -8833
  157. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +87 -175
  158. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +84 -172
  159. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +84 -172
  160. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +83 -171
  161. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +83 -171
  162. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +8978 -0
  163. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +83 -171
  164. nautobot/project-static/docs/user-guide/administration/installation/docker.html +3 -3
  165. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +86 -174
  166. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +3 -3
  167. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +84 -174
  168. nautobot/project-static/docs/user-guide/administration/installation/index.html +85 -173
  169. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +83 -171
  170. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +99 -184
  171. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +3 -3
  172. nautobot/project-static/docs/user-guide/administration/installation/services.html +85 -173
  173. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +13 -9577
  174. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +13 -9560
  175. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +13 -9064
  176. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +85 -173
  177. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +83 -171
  178. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +89 -177
  179. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +86 -174
  180. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +89 -177
  181. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +86 -174
  182. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +86 -174
  183. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +86 -174
  184. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +86 -174
  185. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +86 -174
  186. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +86 -174
  187. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +88 -176
  188. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +86 -174
  189. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +83 -171
  190. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +83 -171
  191. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +83 -171
  192. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +83 -171
  193. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +83 -171
  194. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +83 -171
  195. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +83 -171
  196. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +83 -171
  197. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +83 -171
  198. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +83 -171
  199. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +83 -171
  200. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +83 -171
  201. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +83 -171
  202. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +83 -171
  203. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +83 -171
  204. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +83 -171
  205. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +83 -171
  206. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +83 -171
  207. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +83 -171
  208. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +83 -171
  209. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +83 -171
  210. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +83 -171
  211. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +83 -171
  212. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +83 -171
  213. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +83 -171
  214. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +83 -171
  215. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +83 -171
  216. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +83 -171
  217. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +83 -171
  218. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +83 -171
  219. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +83 -171
  220. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +83 -171
  221. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +83 -171
  222. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +83 -171
  223. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +83 -171
  224. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +83 -171
  225. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +83 -171
  226. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +83 -171
  227. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +84 -172
  228. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +83 -171
  229. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +83 -171
  230. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +83 -171
  231. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +83 -171
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +83 -171
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +83 -171
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +83 -171
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +83 -171
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +83 -171
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +83 -171
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +83 -171
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +83 -171
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +83 -171
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +83 -171
  242. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +84 -172
  243. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +83 -171
  244. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +83 -171
  245. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +83 -171
  246. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +83 -171
  247. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +83 -171
  248. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +83 -171
  249. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +83 -171
  250. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +83 -171
  251. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +83 -171
  252. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +83 -171
  253. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +83 -171
  254. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +83 -171
  255. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +83 -171
  256. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +83 -171
  257. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +83 -171
  258. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +83 -171
  259. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +83 -171
  260. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +83 -171
  261. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +83 -171
  262. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +83 -171
  263. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +83 -171
  264. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +83 -171
  265. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +83 -171
  266. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +83 -171
  267. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +83 -171
  268. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +83 -171
  269. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +83 -171
  270. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +83 -171
  271. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +83 -171
  272. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +83 -171
  273. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +83 -171
  274. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +84 -172
  275. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +83 -171
  276. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +83 -171
  277. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +83 -171
  278. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +83 -171
  279. nautobot/project-static/docs/user-guide/index.html +83 -171
  280. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +83 -171
  281. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +83 -171
  282. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +83 -171
  283. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +83 -171
  284. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +84 -172
  285. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +83 -171
  286. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +83 -171
  287. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +84 -172
  288. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +85 -173
  289. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +83 -171
  290. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +84 -172
  291. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +83 -171
  292. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +83 -171
  293. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +83 -171
  294. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +83 -171
  295. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +83 -171
  296. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +84 -172
  297. nautobot/project-static/docs/user-guide/platform-functionality/note.html +83 -171
  298. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +83 -171
  299. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +83 -171
  300. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +84 -172
  301. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +84 -172
  302. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +85 -173
  303. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +83 -171
  304. nautobot/project-static/docs/user-guide/platform-functionality/role.html +83 -171
  305. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +83 -171
  306. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +83 -171
  307. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +83 -171
  308. nautobot/project-static/docs/user-guide/platform-functionality/status.html +83 -171
  309. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +83 -171
  310. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +83 -171
  311. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +83 -171
  312. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +83 -171
  313. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +83 -171
  314. nautobot/virtualization/filters.py +6 -1
  315. nautobot/virtualization/tables.py +2 -2
  316. {nautobot-2.3.2.dist-info → nautobot-2.3.4.dist-info}/METADATA +4 -4
  317. {nautobot-2.3.2.dist-info → nautobot-2.3.4.dist-info}/RECORD +321 -317
  318. nautobot/project-static/docs/assets/javascripts/bundle.fe8b6f2b.min.js +0 -29
  319. nautobot/project-static/docs/assets/javascripts/bundle.fe8b6f2b.min.js.map +0 -7
  320. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +0 -1
  321. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +0 -1
  322. nautobot/project-static/docs/user-guide/administration/configuration/render-settings-fragment.j2 +0 -76
  323. {nautobot-2.3.2.dist-info → nautobot-2.3.4.dist-info}/LICENSE.txt +0 -0
  324. {nautobot-2.3.2.dist-info → nautobot-2.3.4.dist-info}/NOTICE +0 -0
  325. {nautobot-2.3.2.dist-info → nautobot-2.3.4.dist-info}/WHEEL +0 -0
  326. {nautobot-2.3.2.dist-info → nautobot-2.3.4.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Mapping
2
- from datetime import datetime
2
+ from datetime import datetime, timedelta
3
3
  import logging
4
4
  from pathlib import Path
5
5
 
@@ -83,7 +83,7 @@ class NautobotScheduleEntry(ModelEntry):
83
83
  # This will trigger the job to run at start_time
84
84
  # and avoid the heap block.
85
85
  if model.start_time:
86
- model.last_run_at = model.last_run_at - datetime.timedelta(days=365 * 30)
86
+ model.last_run_at = model.last_run_at - timedelta(days=365 * 30)
87
87
 
88
88
  self.last_run_at = model.last_run_at
89
89
 
nautobot/core/settings.py CHANGED
@@ -641,6 +641,8 @@ LOGIN_REDIRECT_URL = "home"
641
641
  CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend"
642
642
  CONSTANCE_DATABASE_PREFIX = "constance:nautobot:"
643
643
  CONSTANCE_DATABASE_CACHE_BACKEND = "default"
644
+ # Constance defaults to a 24-hour timeout when autofilling its cache, which is undesirable in many cases.
645
+ CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT = int(os.getenv("NAUTOBOT_CACHES_TIMEOUT", "300"))
644
646
  CONSTANCE_IGNORE_ADMIN_VERSION_CHECK = True # avoid potential errors in a multi-node deployment
645
647
 
646
648
  CONSTANCE_ADDITIONAL_FIELDS = {
@@ -853,7 +855,7 @@ CACHES = {
853
855
  "django_prometheus.cache.backends.redis.RedisCache" if METRICS_ENABLED else "django_redis.cache.RedisCache",
854
856
  ),
855
857
  "LOCATION": parse_redis_connection(redis_database=1),
856
- "TIMEOUT": 300,
858
+ "TIMEOUT": int(os.getenv("NAUTOBOT_CACHES_TIMEOUT", "300")),
857
859
  "OPTIONS": {
858
860
  "CLIENT_CLASS": "django_redis.client.DefaultClient",
859
861
  "PASSWORD": "",
@@ -47,7 +47,7 @@ properties:
47
47
  description: >-
48
48
  A list of valid fully-qualified domain names (FQDNs) and/or IP addresses that can be used to reach the
49
49
  Nautobot service. (If provided as an environment variable, it should be a space-separated string, for example
50
- `NAUTOBOT_ALLOWED_HOSTS="localhost 127.0.0.1 example.com"`)
50
+ `NAUTOBOT_ALLOWED_HOSTS="localhost 127.0.0.1 nautobot.example.com"`)
51
51
  details: |-
52
52
  Usually this is the same as the hostname for the Nautobot server, but can also be different; for example,
53
53
  when using a reverse proxy serving the Nautobot website under a different FQDN than the hostname of the
@@ -70,7 +70,7 @@ properties:
70
70
 
71
71
  !!! tip
72
72
  If there is more than one hostname in this list, you may also need to set
73
- [CSRF_TRUSTED_ORIGINS](optional-settings.md#csrf_trusted_origins) as well.
73
+ [CSRF_TRUSTED_ORIGINS](settings.md#csrf_trusted_origins) as well.
74
74
 
75
75
  If you are not yet sure what the domain name and/or IP address of the Nautobot installation will be,
76
76
  and are comfortable accepting the risks in doing so, you can set this to a wildcard (asterisk) to
@@ -83,7 +83,6 @@ properties:
83
83
  !!! warning
84
84
  It is not recommended to leave this value as `['*']` for production deployments.
85
85
  environment_variable: "NAUTOBOT_ALLOWED_HOSTS"
86
- is_required_setting: true
87
86
  items:
88
87
  type: "string"
89
88
  see_also:
@@ -297,19 +296,21 @@ properties:
297
296
  TIMEOUT: 300
298
297
  description: "The `CACHES` setting is required to simplify the configuration for `django-redis`."
299
298
  details: |-
300
- The [`django-redis`](https://github.com/jazzband/django-redis) Django plugin is used to enable Redis
301
- as a concurrent write lock for preventing race conditions when allocating IP address objects.
299
+ The [`django-redis`](https://github.com/jazzband/django-redis) Django plugin is used to enable Redis as a
300
+ concurrent write lock for preventing race conditions when allocating IP address objects.
301
+ Nautobot also uses the built-in [Django cache framework](https://docs.djangoproject.com/en/stable/topics/cache/)
302
+ (which also relies on the `CACHES` setting) to perform caching. This includes caching of the values of
303
+ [administratively configurable settings](#administratively-configurable-settings) as stored in the database.
302
304
 
303
- !!! important
304
- Nautobot also utilizes the built-in
305
- [Django cache framework](https://docs.djangoproject.com/en/stable/topics/cache/)
306
- (which also relies on the `CACHES` setting) to perform caching.
307
-
308
- +/- 2.0.0
309
- The default value of `CACHES["default"]["LOCATION"]` has changed from `redis://localhost:6379/0`
310
- to `redis://localhost:6379/1`, as Django's native caching is now taking the role previously occupied by
311
- `django-cacheops`.
312
- is_required_setting: true
305
+ !!! tip
306
+ Rather than directly setting `CACHES["default"]["LOCATION"]`, we recommend managing this setting via
307
+ the various `NAUTOBOT_REDIS_*` environment variables, as those variables apply to both `CACHES` and
308
+ [`CELERY_BROKER_URL`](#celery_broker_url) alike, which is typically preferable.
309
+
310
+ +++ 2.3.4 "`NAUTOBOT_CACHES_TIMEOUT` environment variable"
311
+ Added support for the environment variable `NAUTOBOT_CACHES_TIMEOUT` for configuring the
312
+ `CACHES["default"]["TIMEOUT"]` setting. This environment variable also controls the cache timeout
313
+ for administratively configurable settings.
313
314
  properties:
314
315
  default:
315
316
  properties:
@@ -322,6 +323,13 @@ properties:
322
323
  type: "string"
323
324
  LOCATION:
324
325
  default: "redis://localhost:6379/1"
326
+ environment_variables:
327
+ - "NAUTOBOT_REDIS_SCHEME"
328
+ - "NAUTOBOT_REDIS_SSL"
329
+ - "NAUTOBOT_REDIS_USERNAME"
330
+ - "NAUTOBOT_REDIS_PASSWORD"
331
+ - "NAUTOBOT_REDIS_HOST"
332
+ - "NAUTOBOT_REDIS_PORT"
325
333
  format: "uri"
326
334
  type: "string"
327
335
  OPTIONS:
@@ -336,10 +344,11 @@ properties:
336
344
  type: "object"
337
345
  TIMEOUT:
338
346
  default: 300
347
+ environment_variable: "NAUTOBOT_CACHES_TIMEOUT"
339
348
  type: "integer"
340
349
  type: "object"
341
350
  see_also:
342
- "Guide to Nautobot Caching, including TLS and HA configuration": "../../administration/guides/caching.md"
351
+ "Guide to Nautobot Redis configuration, including TLS and HA configuration": "redis.md"
343
352
  type: "object"
344
353
  CELERY_BEAT_HEARTBEAT_FILE:
345
354
  default: "/tmp/nautobot_celery_beat_heartbeat"
@@ -351,8 +360,7 @@ properties:
351
360
  default: {}
352
361
  description: "A dict of additional options passed to the Celery broker transport."
353
362
  details: >-
354
- This is only required when
355
- [configuring Celery to utilize Redis Sentinel](../../administration/guides/caching.md#celery-sentinel-configuration).
363
+ This is only required when [configuring Celery to utilize Redis Sentinel](redis.md#celery-sentinel-configuration).
356
364
  properties:
357
365
  master_name:
358
366
  type: "string"
@@ -366,7 +374,18 @@ properties:
366
374
  CELERY_BROKER_URL:
367
375
  default: "redis://localhost:6379/0"
368
376
  description: "Celery broker URL used to tell workers where queues are located."
369
- environment_variable: "NAUTOBOT_CELERY_BROKER_URL"
377
+ details: >-
378
+ If the `NAUTOBOT_CELERY_BROKER_URL` environment variable is not set, the default for this setting will be
379
+ influenced by the various `NAUTOBOT_REDIS_*` environment variables instead, which is often preferable as those
380
+ variables also influence the [`CACHES`](#caches) configuration as well.
381
+ environment_variables:
382
+ - "NAUTOBOT_CELERY_BROKER_URL"
383
+ - "NAUTOBOT_REDIS_SCHEME"
384
+ - "NAUTOBOT_REDIS_SSL"
385
+ - "NAUTOBOT_REDIS_USERNAME"
386
+ - "NAUTOBOT_REDIS_PASSWORD"
387
+ - "NAUTOBOT_REDIS_HOST"
388
+ - "NAUTOBOT_REDIS_PORT"
370
389
  format: "uri"
371
390
  type: "string"
372
391
  CELERY_BROKER_USE_SSL:
@@ -628,7 +647,6 @@ properties:
628
647
 
629
648
  As of Nautobot 1.1.5 and later, if you have generated a new `nautobot_config.py` using
630
649
  `nautobot-server init`, this line is already present in your config and no action is required.
631
- is_required_setting: true
632
650
  properties:
633
651
  default:
634
652
  additionalProperties: true
@@ -828,7 +846,7 @@ properties:
828
846
  FORCE_SCRIPT_NAME:
829
847
  default: null
830
848
  description: >-
831
- If not None, this will be used as the value of the SCRIPT_NAME environment variable in any HTTP request.
849
+ If not None, this will be used as the value of the `SCRIPT_NAME` environment variable in any HTTP request.
832
850
  details: |-
833
851
  This setting can be used to override the server-provided value of `SCRIPT_NAME`, which is most commonly used
834
852
  for hosting Nautobot in a subdirectory (e.g. _example.com/nautobot/_).
@@ -1205,7 +1223,7 @@ properties:
1205
1223
  !!! note
1206
1224
  The Docker container normally attempts to run migrations on startup; however, if the database is
1207
1225
  in a read-only state the Docker container will fail to start. Setting the environment variable
1208
- [`NAUTOBOT_DOCKER_SKIP_INIT`](../installation-extras/docker.md#nautobot_docker_skip_init) to `true`
1226
+ [`NAUTOBOT_DOCKER_SKIP_INIT`](../guides/docker.md#nautobot_docker_skip_init) to `true`
1209
1227
  will prevent the migrations from occurring.
1210
1228
 
1211
1229
  !!! note
@@ -1609,7 +1627,6 @@ properties:
1609
1627
  In the case of a highly available installation with multiple web servers, `SECRET_KEY` must be identical
1610
1628
  among all servers in order to maintain a persistent user session state.
1611
1629
  environment_variable: "NAUTOBOT_SECRET_KEY"
1612
- is_required_setting: true
1613
1630
  type: "string"
1614
1631
  SESSION_CACHE_ALIAS:
1615
1632
  default: "default"
@@ -1,6 +1,13 @@
1
1
  import copy
2
2
  import hashlib
3
3
 
4
+ try:
5
+ from coverage import Coverage
6
+
7
+ has_coverage = True
8
+ except ImportError:
9
+ has_coverage = False
10
+
4
11
  from django.conf import settings
5
12
  from django.core.management import call_command
6
13
  from django.db import connections
@@ -49,6 +56,15 @@ class NautobotTestRunner(DiscoverRunner):
49
56
 
50
57
  exclude_tags = ["integration"]
51
58
 
59
+ @classmethod
60
+ def add_arguments(cls, parser):
61
+ super().add_arguments(parser)
62
+ parser.add_argument(
63
+ "--cache-test-fixtures",
64
+ action="store_true",
65
+ help="Save test database to a json fixture file to re-use on subsequent tests.",
66
+ )
67
+
52
68
  def __init__(self, cache_test_fixtures=False, **kwargs):
53
69
  self.cache_test_fixtures = cache_test_fixtures
54
70
 
@@ -64,15 +80,6 @@ class NautobotTestRunner(DiscoverRunner):
64
80
 
65
81
  super().__init__(**kwargs)
66
82
 
67
- @classmethod
68
- def add_arguments(cls, parser):
69
- super().add_arguments(parser)
70
- parser.add_argument(
71
- "--cache-test-fixtures",
72
- action="store_true",
73
- help="Save test database to a json fixture file to re-use on subsequent tests.",
74
- )
75
-
76
83
  def setup_test_environment(self, **kwargs):
77
84
  super().setup_test_environment(**kwargs)
78
85
  # Remove 'testserver' that Django "helpfully" adds automatically to ALLOWED_HOSTS, masking issues like #3065
@@ -91,6 +98,13 @@ class NautobotTestRunner(DiscoverRunner):
91
98
 
92
99
  old_names = []
93
100
 
101
+ # Nautobot specific - disable coverage measurement to improve performance of (slow) database setup
102
+ cov = None
103
+ if has_coverage:
104
+ cov = Coverage.current()
105
+ if cov is not None:
106
+ cov.stop()
107
+
94
108
  for db_name, aliases in test_databases.values():
95
109
  first_alias = None
96
110
  for alias in aliases:
@@ -150,6 +164,10 @@ class NautobotTestRunner(DiscoverRunner):
150
164
  for alias in connections:
151
165
  connections[alias].force_debug_cursor = True
152
166
 
167
+ # Nautobot specific - resume test coverage measurement
168
+ if cov is not None:
169
+ cov.start()
170
+
153
171
  return old_names
154
172
 
155
173
  def teardown_databases(self, old_config, **kwargs):
@@ -35,10 +35,13 @@ SETTINGS_DOCUMENTATION_SCHEMA = {
35
35
  "environment_variable": {
36
36
  "type": "string",
37
37
  },
38
- "is_constance_config": {
39
- "type": "boolean",
38
+ "environment_variables": {
39
+ "type": "array",
40
+ "items": {
41
+ "type": "string",
42
+ },
40
43
  },
41
- "is_required_setting": {
44
+ "is_constance_config": {
42
45
  "type": "boolean",
43
46
  },
44
47
  "see_also": {
@@ -122,6 +125,7 @@ class SettingsJSONSchemaTestCase(TestCase):
122
125
  "CONSTANCE_BACKEND",
123
126
  "CONSTANCE_CONFIG",
124
127
  "CONSTANCE_CONFIG_FIELDSETS",
128
+ "CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT",
125
129
  "CONSTANCE_DATABASE_CACHE_BACKEND",
126
130
  "CONSTANCE_DATABASE_PREFIX",
127
131
  "CSRF_FAILURE_VIEW",
@@ -234,6 +234,19 @@ class GetFooForModelTest(TestCase):
234
234
  self.assertEqual(lookup.get_model_from_name("dcim.device"), dcim_models.Device)
235
235
  self.assertEqual(lookup.get_model_from_name("dcim.location"), dcim_models.Location)
236
236
 
237
+ def test_get_model_for_view_name(self):
238
+ """
239
+ Test the util function `get_model_for_view_name` returns the appropriate Model, if the colon separated view name provided.
240
+ """
241
+ with self.subTest("Test core view."):
242
+ self.assertEqual(lookup.get_model_for_view_name("dcim:device_list"), dcim_models.Device)
243
+ with self.subTest("Test app view."):
244
+ self.assertEqual(lookup.get_model_for_view_name("plugins:example_app:examplemodel_list"), ExampleModel)
245
+ with self.subTest("Test unexpected view."):
246
+ with self.assertRaises(ValueError) as err:
247
+ lookup.get_model_for_view_name("unknown:plugins:example_app:examplemodel_list")
248
+ self.assertEqual(str(err.exception), "Unexpected View Name: unknown:plugins:example_app:examplemodel_list")
249
+
237
250
 
238
251
  class IsTaggableTest(TestCase):
239
252
  def test_is_taggable_true(self):
@@ -213,7 +213,13 @@ def get_model_for_view_name(view_name):
213
213
  Return the model class associated with the given view_name e.g. "circuits:circuit_detail", "dcim:device_list" and etc.
214
214
  If the app_label or model_name contained by the given view_name is invalid, this will return `None`.
215
215
  """
216
- app_label, model_name = view_name.split(":") # dcim, device_list
216
+ split_view_name = view_name.split(":")
217
+ if len(split_view_name) == 2:
218
+ app_label, model_name = split_view_name # dcim, device_list
219
+ elif len(split_view_name) == 3:
220
+ _, app_label, model_name = split_view_name # plugins, app_name, model_list
221
+ else:
222
+ raise ValueError(f"Unexpected View Name: {view_name}")
217
223
  model_name = model_name.split("_")[0] # device
218
224
 
219
225
  try:
@@ -29,7 +29,7 @@ class PowerPanelTable(BaseTable):
29
29
  location = tables.Column(linkify=True)
30
30
  power_feed_count = LinkedCountColumn(
31
31
  viewname="dcim:powerfeed_list",
32
- url_params={"power_panel_id": "pk"},
32
+ url_params={"power_panel": "pk"},
33
33
  verbose_name="Feeds",
34
34
  )
35
35
  tags = TagColumn(url_name="dcim:powerpanel_list")
@@ -3349,7 +3349,7 @@ class CableTestCase(FilterTestCases.FilterTestCase):
3349
3349
 
3350
3350
  def test_device(self):
3351
3351
  """Test that the device filter returns all cables for a device and its modules."""
3352
- interfaces = Interface.objects.filter(cable__isnull=True)[:3]
3352
+ interfaces = list(Interface.objects.filter(cable__isnull=True)[:3])
3353
3353
  manufacturer = Manufacturer.objects.first()
3354
3354
  device_type = DeviceType.objects.create(
3355
3355
  manufacturer=manufacturer, model="Test Device Filter for Cable Device Type"
@@ -175,11 +175,12 @@ def web_request_context(
175
175
  :param user: User object
176
176
  :param context_detail: Optional extra details about the transaction (ex: the plugin name that initiated the change)
177
177
  :param change_id: Optional uuid object to uniquely identify the transaction. One will be generated if not supplied
178
- :param context: Optional string value of the generated change log entries' "change_context" field, defaults to ObjectChangeEventContextChoices.CONTEXT_ORM.
179
- Valid choices are in nautobot.extras.choices.ObjectChangeEventContextChoices
178
+ :param context: Optional string value of the generated change log entries' "change_context" field.
179
+ Defaults to `ObjectChangeEventContextChoices.CONTEXT_ORM`.
180
+ Valid choices are in `nautobot.extras.choices.ObjectChangeEventContextChoices`.
180
181
  :param request: Optional web request instance, one will be generated if not supplied
181
182
  """
182
- from nautobot.extras.jobs import enqueue_job_hooks # prevent circular import
183
+ from nautobot.extras.jobs import enqueue_job_hooks, get_jobs # prevent circular import
183
184
 
184
185
  valid_contexts = {
185
186
  ObjectChangeEventContextChoices.CONTEXT_JOB: JobChangeContext,
@@ -202,9 +203,15 @@ def web_request_context(
202
203
  with change_logging(change_context):
203
204
  yield request
204
205
  finally:
206
+ jobs_refreshed = False
205
207
  # enqueue jobhooks and webhooks, use change_context.change_id in case change_id was not supplied
206
208
  for object_change in ObjectChange.objects.filter(request_id=change_context.change_id).iterator():
207
- enqueue_job_hooks(object_change)
209
+ if context != ObjectChangeEventContextChoices.CONTEXT_JOB_HOOK:
210
+ # Make sure JobHooks are up to date (once) before calling them
211
+ if not jobs_refreshed:
212
+ get_jobs(reload=True)
213
+ jobs_refreshed = True
214
+ enqueue_job_hooks(object_change)
208
215
  enqueue_webhooks(object_change)
209
216
 
210
217
 
nautobot/extras/jobs.py CHANGED
@@ -1168,7 +1168,6 @@ def enqueue_job_hooks(object_change):
1168
1168
  job_hooks = JobHook.objects.filter(content_types=content_type, enabled=True, **{action_flag: True})
1169
1169
 
1170
1170
  # Enqueue the jobs related to the job_hooks
1171
- get_jobs(reload=True)
1172
1171
  for job_hook in job_hooks:
1173
1172
  job_model = job_hook.job
1174
1173
  if not job_model.installed or not job_model.enabled:
@@ -251,7 +251,10 @@ class DynamicGroup(PrimaryModel):
251
251
  # Skip filter fields that have methods defined. They are not reversible.
252
252
  if skip_method_filters and filterset_field.method is not None:
253
253
  # Don't skip method fields that also have a "generate_query_" method
254
- if hasattr(filterset, "generate_query_" + filterset_field.method):
254
+ query_attr = (
255
+ filterset_field.method.__name__ if callable(filterset_field.method) else filterset_field.method
256
+ )
257
+ if hasattr(filterset, f"generate_query_{query_attr}"):
255
258
  logger.debug(
256
259
  "Keeping %s for filterform: has a `generate_query_` filter method", filterset_field_name
257
260
  )
@@ -1,3 +1,5 @@
1
+ from unittest import mock
2
+
1
3
  from django.contrib.auth import get_user_model
2
4
  from django.contrib.contenttypes.models import ContentType
3
5
  from django.test import TestCase
@@ -72,7 +74,9 @@ class WebRequestContextTestCase(TestCase):
72
74
  self.assertEqual(oc_list[0].changed_object, location)
73
75
  self.assertEqual(oc_list[0].action, ObjectChangeActionChoices.ACTION_CREATE)
74
76
 
75
- def test_create_then_delete(self):
77
+ @mock.patch("nautobot.extras.jobs.enqueue_job_hooks")
78
+ @mock.patch("nautobot.extras.context_managers.enqueue_webhooks")
79
+ def test_create_then_delete(self, mock_enqueue_webhooks, mock_enqueue_job_hooks):
76
80
  """Test that a create followed by a delete is logged as two changes"""
77
81
  location_type = LocationType.objects.get(name="Campus")
78
82
  location_status = Status.objects.get_for_model(Location).first()
@@ -88,6 +92,8 @@ class WebRequestContextTestCase(TestCase):
88
92
  self.assertEqual(len(oc_list), 2)
89
93
  self.assertEqual(oc_list[0].action, ObjectChangeActionChoices.ACTION_DELETE)
90
94
  self.assertEqual(oc_list[1].action, ObjectChangeActionChoices.ACTION_CREATE)
95
+ mock_enqueue_job_hooks.assert_has_calls([mock.call(oc_list[0]), mock.call(oc_list[1])])
96
+ mock_enqueue_webhooks.assert_has_calls([mock.call(oc_list[0]), mock.call(oc_list[1])])
91
97
 
92
98
  def test_update_then_delete(self):
93
99
  """Test that an update followed by a delete is logged as a single delete"""
@@ -179,21 +185,30 @@ class WebRequestContextTestCase(TestCase):
179
185
  with self.subTest():
180
186
  self.assertEqual(oc_list[0].change_context_detail, "test_change_log_context")
181
187
 
182
- def test_change_webhook_enqueued(self):
188
+ @mock.patch("nautobot.extras.webhooks.process_webhook.apply_async")
189
+ def test_change_webhook_enqueued(self, mock_apply_async):
183
190
  """Test that the webhook resides on the queue"""
184
- # TODO(john): come back to this with a way to actually do it without a running worker
185
- # The celery inspection API expects to be able to communicate with at least 1 running
186
- # worker and there does not appear to be an easy way to look into the queues directly.
187
- # with web_request_context(self.user):
188
- # site = Site(name="Test Site 2")
189
- # site.save()
191
+ with web_request_context(self.user):
192
+ location = Location(
193
+ name="Test Location 2",
194
+ location_type=LocationType.objects.get(name="Campus"),
195
+ status=Status.objects.get_for_model(Location).first(),
196
+ )
197
+ location.save()
190
198
 
191
199
  # Verify that a job was queued for the object creation webhook
192
- # site = Site.objects.get(name="Test Site 2")
193
-
194
- # self.assertEqual(job.args[0], Webhook.objects.get(type_create=True))
195
- # self.assertEqual(job.args[1]["id"], str(site.pk))
196
- # self.assertEqual(job.args[2], "site")
200
+ oc_list = get_changes_for_model(location)
201
+ mock_apply_async.assert_called_once()
202
+ call_args = mock_apply_async.call_args.kwargs["args"]
203
+ self.assertEqual(8, len(call_args), call_args)
204
+ self.assertEqual(call_args[0], Webhook.objects.get(type_create=True).pk)
205
+ self.assertEqual(call_args[1], oc_list[0].object_data_v2)
206
+ self.assertEqual(call_args[2], "location")
207
+ self.assertEqual(call_args[3], "create")
208
+ self.assertIsInstance(call_args[4], str) # str(timezone.now())
209
+ self.assertEqual(call_args[5], self.user.username)
210
+ self.assertEqual(call_args[6], oc_list[0].request_id)
211
+ self.assertEqual(call_args[7], oc_list[0].get_snapshots())
197
212
 
198
213
 
199
214
  class WebRequestContextTransactionTestCase(TransactionTestCase):
@@ -282,7 +297,8 @@ class BulkEditDeleteChangeLogging(TestCase):
282
297
  self.assertIsNone(snapshots["differences"]["removed"])
283
298
  self.assertEqual(snapshots["differences"]["added"]["description"], "changed")
284
299
 
285
- def test_bulk_edit(self):
300
+ @mock.patch("nautobot.extras.jobs.import_jobs")
301
+ def test_bulk_edit(self, mock_import_jobs):
286
302
  """Test that edits to multiple objects are correctly logged"""
287
303
  location_type = LocationType.objects.get(name="Campus")
288
304
  location_status = Status.objects.get_for_model(Location).first()
@@ -309,6 +325,9 @@ class BulkEditDeleteChangeLogging(TestCase):
309
325
  self.assertIsNone(snapshots["differences"]["removed"])
310
326
  self.assertEqual(snapshots["differences"]["added"]["description"], "changed")
311
327
 
328
+ # Check for regression of https://github.com/nautobot/nautobot/issues/6203
329
+ mock_import_jobs.assert_called_once()
330
+
312
331
  def test_bulk_edit_device_type_software_image_file(self):
313
332
  """Test that bulk edits to null does not cause integrity error"""
314
333
  manufacturer = Manufacturer.objects.create(name="Test")
@@ -494,6 +494,17 @@ class DynamicGroupModelTest(DynamicGroupTestBase): # TODO: BaseModelTestCase mi
494
494
  self.assertIsInstance(fields["cluster"], MultiMatchModelMultipleChoiceField)
495
495
  self.assertIsInstance(fields["cluster"].widget, APISelectMultiple)
496
496
 
497
+ def test_map_filter_fields_with_custom_filter_method(self):
498
+ """
499
+ Test that extension_filters with custom methods can be concatenated into `generate_query_{filter_method}`
500
+ and _map_filter_fields method doesn't brake on string concatenation.
501
+ This is a regression test for issue #6184.
502
+ """
503
+ tenant_content_type = ContentType.objects.get_for_model(Tenant)
504
+ # using Tenant as example app has a custom filter with a custom method.
505
+ group = DynamicGroup(name="tenant", content_type=tenant_content_type)
506
+ self.assertIsInstance(group._map_filter_fields, dict)
507
+
497
508
  def test_map_filter_fields_skip_missing(self):
498
509
  """
499
510
  Test that missing fields are skipped in `DynamicGroup._map_filter_fields`
@@ -1091,12 +1091,15 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1091
1091
  cls.app_job = JobModel.objects.get(job_class_name="ExampleJob")
1092
1092
 
1093
1093
  def test_job_class(self):
1094
+ self.assertIsNotNone(self.local_job.job_class)
1094
1095
  self.assertEqual(self.local_job.job_class.description, "Validate job import")
1095
1096
 
1097
+ self.assertIsNotNone(self.app_job.job_class)
1096
1098
  self.assertEqual(self.app_job.job_class, ExampleJob)
1097
1099
 
1098
1100
  def test_class_path(self):
1099
1101
  self.assertEqual(self.local_job.class_path, "pass.TestPass")
1102
+ self.assertIsNotNone(self.local_job.job_class)
1100
1103
  self.assertEqual(self.local_job.class_path, self.local_job.job_class.class_path)
1101
1104
 
1102
1105
  self.assertEqual(self.app_job.class_path, "example_app.jobs.ExampleJob")
@@ -1118,6 +1121,7 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1118
1121
  self.assertTrue(job_model.enabled)
1119
1122
  else:
1120
1123
  self.assertFalse(job_model.enabled)
1124
+ self.assertIsNotNone(job_model.job_class)
1121
1125
  for field_name in JOB_OVERRIDABLE_FIELDS:
1122
1126
  if field_name == "name" and "duplicate_name" in job_model.job_class.__module__:
1123
1127
  pass # name field for test_duplicate_name jobs tested in test_duplicate_job_name below
@@ -1173,6 +1177,7 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1173
1177
  setattr(self.job_containing_sensitive_variables, f"{field_name}_override", False)
1174
1178
  self.job_containing_sensitive_variables.validated_save()
1175
1179
  self.job_containing_sensitive_variables.refresh_from_db()
1180
+ self.assertIsNotNone(self.job_containing_sensitive_variables.job_class)
1176
1181
  for field_name in overridden_attrs:
1177
1182
  self.assertEqual(
1178
1183
  getattr(self.job_containing_sensitive_variables, field_name),
@@ -3008,6 +3008,7 @@ class JobCustomTemplateTestCase(TestCase):
3008
3008
  cls.run_url = reverse("extras:job_run", kwargs={"pk": cls.example_job.pk})
3009
3009
 
3010
3010
  def test_rendering_custom_template(self):
3011
+ self.assertIsNotNone(self.example_job.job_class)
3011
3012
  obj_perm = ObjectPermission(name="Test permission", actions=["view", "run"])
3012
3013
  obj_perm.save()
3013
3014
  obj_perm.users.add(self.user)
nautobot/ipam/models.py CHANGED
@@ -535,6 +535,10 @@ class Prefix(PrimaryModel):
535
535
  def __str__(self):
536
536
  return str(self.prefix)
537
537
 
538
+ @property
539
+ def display(self):
540
+ return f"{self.prefix}: {self.namespace}"
541
+
538
542
  def _deconstruct_prefix(self, prefix):
539
543
  if prefix:
540
544
  if isinstance(prefix, str):
@@ -443,6 +443,17 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
443
443
  for i, p in enumerate(response.data):
444
444
  self.assertEqual(p["prefix"], str(available_prefixes[i]))
445
445
 
446
+ def test_prefix_display_value(self):
447
+ """
448
+ Test that the `display` field is correctly populated.
449
+ """
450
+ url = reverse("ipam-api:prefix-list")
451
+ self.add_permissions("ipam.view_prefix")
452
+
453
+ response = self.client.get(f"{url}?depth=1", **self.header)
454
+ for p in response.data["results"]:
455
+ self.assertEqual(p["display"], f'{p["prefix"]}: {p["namespace"]["name"]}')
456
+
446
457
  def test_create_single_available_prefix(self):
447
458
  """
448
459
  Test retrieval of the first available prefix within a parent prefix.