nautobot 2.1.7__py3-none-any.whl → 2.1.9__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 (338) hide show
  1. nautobot/apps/api.py +1 -2
  2. nautobot/apps/utils.py +4 -0
  3. nautobot/apps/views.py +2 -0
  4. nautobot/circuits/api/urls.py +1 -2
  5. nautobot/circuits/api/views.py +0 -12
  6. nautobot/circuits/tests/integration/test_relationships.py +0 -4
  7. nautobot/core/api/routers.py +25 -3
  8. nautobot/core/api/utils.py +4 -0
  9. nautobot/core/api/views.py +21 -15
  10. nautobot/core/celery/schedulers.py +13 -0
  11. nautobot/core/choices.py +0 -21
  12. nautobot/core/models/__init__.py +1 -1
  13. nautobot/core/models/tree_queries.py +29 -7
  14. nautobot/core/releases.py +1 -1
  15. nautobot/core/settings.py +9 -0
  16. nautobot/core/settings_funcs.py +0 -18
  17. nautobot/core/signals.py +5 -5
  18. nautobot/core/tasks.py +7 -3
  19. nautobot/core/templates/admin/base.html +23 -94
  20. nautobot/core/templates/generic/object_list.html +2 -0
  21. nautobot/core/templates/graphene/graphiql.html +18 -47
  22. nautobot/core/templates/inc/footer.html +5 -5
  23. nautobot/core/templates/inc/nav_menu.html +0 -7
  24. nautobot/core/templates/nautobot_config.py.j2 +6 -0
  25. nautobot/core/templates/rest_framework/api.html +12 -5
  26. nautobot/core/testing/mixins.py +13 -5
  27. nautobot/core/tests/integration/test_plugin_navbar.py +7 -21
  28. nautobot/core/tests/integration/test_view_authentication.py +67 -0
  29. nautobot/core/tests/runner.py +25 -2
  30. nautobot/core/tests/test_graphql.py +2 -14
  31. nautobot/core/tests/test_models.py +3 -3
  32. nautobot/core/tests/test_navigations.py +67 -10
  33. nautobot/core/tests/test_releases.py +9 -3
  34. nautobot/core/tests/test_views.py +23 -16
  35. nautobot/core/utils/lookup.py +124 -0
  36. nautobot/core/views/__init__.py +3 -7
  37. nautobot/core/views/generic.py +9 -0
  38. nautobot/dcim/api/urls.py +1 -2
  39. nautobot/dcim/api/views.py +1 -12
  40. nautobot/dcim/choices.py +56 -0
  41. nautobot/dcim/models/racks.py +1 -3
  42. nautobot/dcim/navigation.py +1 -1
  43. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +67 -43
  44. nautobot/dcim/tests/test_api.py +3 -0
  45. nautobot/dcim/tests/test_filters.py +0 -28
  46. nautobot/dcim/views.py +5 -2
  47. nautobot/extras/api/urls.py +1 -2
  48. nautobot/extras/api/views.py +0 -10
  49. nautobot/extras/choices.py +14 -0
  50. nautobot/extras/models/customfields.py +93 -34
  51. nautobot/extras/models/groups.py +1 -1
  52. nautobot/extras/models/relationships.py +32 -19
  53. nautobot/extras/navigation.py +3 -2
  54. nautobot/extras/plugins/__init__.py +8 -0
  55. nautobot/extras/plugins/views.py +6 -9
  56. nautobot/extras/querysets.py +1 -1
  57. nautobot/extras/signals.py +12 -6
  58. nautobot/extras/templates/extras/customfield.html +22 -14
  59. nautobot/extras/templatetags/job_buttons.py +7 -0
  60. nautobot/extras/templatetags/plugins.py +5 -1
  61. nautobot/extras/tests/test_customfields.py +323 -287
  62. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  63. nautobot/extras/tests/test_jobs.py +2 -2
  64. nautobot/extras/tests/test_plugins.py +41 -0
  65. nautobot/extras/tests/test_relationships.py +31 -14
  66. nautobot/extras/tests/test_views.py +124 -1
  67. nautobot/extras/utils.py +7 -3
  68. nautobot/extras/views.py +10 -10
  69. nautobot/ipam/api/urls.py +1 -2
  70. nautobot/ipam/api/views.py +6 -13
  71. nautobot/ipam/tables.py +0 -1
  72. nautobot/ipam/tests/test_graphql.py +2 -3
  73. nautobot/ipam/views.py +12 -10
  74. nautobot/project-static/css/base.css +1 -0
  75. nautobot/project-static/docs/404.html +30 -2
  76. nautobot/project-static/docs/apps/index.html +30 -2
  77. nautobot/project-static/docs/apps/nautobot-apps.html +30 -2
  78. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +30 -2
  79. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +30 -2
  80. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +410 -410
  81. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +30 -2
  82. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +386 -358
  83. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +30 -2
  84. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +30 -2
  85. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +30 -2
  86. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +30 -2
  87. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +45 -17
  88. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +30 -2
  89. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +30 -2
  90. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +30 -2
  91. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +759 -602
  92. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +30 -2
  93. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +30 -2
  94. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +30 -2
  95. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +528 -467
  96. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +205 -109
  97. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +30 -2
  98. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +1265 -785
  99. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1827 -1746
  100. nautobot/project-static/docs/development/apps/api/configuration-view.html +30 -2
  101. nautobot/project-static/docs/development/apps/api/database-backend-config.html +30 -2
  102. nautobot/project-static/docs/development/apps/api/models/django-admin.html +30 -2
  103. nautobot/project-static/docs/development/apps/api/models/global-search.html +30 -2
  104. nautobot/project-static/docs/development/apps/api/models/graphql.html +30 -2
  105. nautobot/project-static/docs/development/apps/api/models/index.html +30 -2
  106. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +31 -3
  107. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +30 -2
  108. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +30 -2
  109. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +30 -2
  110. nautobot/project-static/docs/development/apps/api/platform-features/index.html +30 -2
  111. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +30 -2
  112. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +30 -2
  113. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +30 -2
  114. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +30 -2
  115. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +30 -2
  116. nautobot/project-static/docs/development/apps/api/prometheus.html +30 -2
  117. nautobot/project-static/docs/development/apps/api/setup.html +30 -2
  118. nautobot/project-static/docs/development/apps/api/testing.html +33 -5
  119. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +30 -2
  120. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +30 -2
  121. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +30 -2
  122. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +33 -5
  123. nautobot/project-static/docs/development/apps/api/ui-extensions/object-detail-views.html +13 -5559
  124. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +5594 -0
  125. nautobot/project-static/docs/development/apps/api/ui-extensions/tabs.html +3 -3
  126. nautobot/project-static/docs/development/apps/api/views/base-template.html +30 -2
  127. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +44 -11
  128. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +47 -14
  129. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +30 -2
  130. nautobot/project-static/docs/development/apps/api/views/index.html +30 -2
  131. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +30 -2
  132. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +30 -2
  133. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +30 -2
  134. nautobot/project-static/docs/development/apps/api/views/notes.html +30 -2
  135. nautobot/project-static/docs/development/apps/api/views/rest-api.html +30 -2
  136. nautobot/project-static/docs/development/apps/api/views/urls.html +30 -2
  137. nautobot/project-static/docs/development/apps/index.html +30 -2
  138. nautobot/project-static/docs/development/apps/migration/code-updates.html +30 -2
  139. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +30 -2
  140. nautobot/project-static/docs/development/apps/migration/from-v1.html +30 -2
  141. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +30 -2
  142. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +30 -2
  143. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +30 -2
  144. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +30 -2
  145. nautobot/project-static/docs/development/apps/porting-from-netbox.html +30 -2
  146. nautobot/project-static/docs/development/core/application-registry.html +30 -2
  147. nautobot/project-static/docs/development/core/best-practices.html +33 -5
  148. nautobot/project-static/docs/development/core/bootstrap-ui.html +30 -2
  149. nautobot/project-static/docs/development/core/caching.html +5481 -0
  150. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +30 -2
  151. nautobot/project-static/docs/development/core/extending-models.html +33 -5
  152. nautobot/project-static/docs/development/core/generic-views.html +30 -2
  153. nautobot/project-static/docs/development/core/getting-started.html +49 -12
  154. nautobot/project-static/docs/development/core/homepage.html +30 -2
  155. nautobot/project-static/docs/development/core/index.html +30 -2
  156. nautobot/project-static/docs/development/core/model-features.html +30 -2
  157. nautobot/project-static/docs/development/core/natural-keys.html +30 -2
  158. nautobot/project-static/docs/development/core/navigation-menu.html +30 -2
  159. nautobot/project-static/docs/development/core/release-checklist.html +30 -2
  160. nautobot/project-static/docs/development/core/role-internals.html +30 -2
  161. nautobot/project-static/docs/development/core/style-guide.html +30 -2
  162. nautobot/project-static/docs/development/core/templates.html +30 -2
  163. nautobot/project-static/docs/development/core/testing.html +30 -2
  164. nautobot/project-static/docs/development/core/user-preferences.html +30 -2
  165. nautobot/project-static/docs/development/index.html +30 -2
  166. nautobot/project-static/docs/development/jobs/index.html +30 -2
  167. nautobot/project-static/docs/development/jobs/migration/from-v1.html +30 -2
  168. nautobot/project-static/docs/index.html +30 -2
  169. nautobot/project-static/docs/objects.inv +0 -0
  170. nautobot/project-static/docs/release-notes/index.html +30 -2
  171. nautobot/project-static/docs/release-notes/version-1.0.html +30 -2
  172. nautobot/project-static/docs/release-notes/version-1.1.html +30 -2
  173. nautobot/project-static/docs/release-notes/version-1.2.html +30 -2
  174. nautobot/project-static/docs/release-notes/version-1.3.html +30 -2
  175. nautobot/project-static/docs/release-notes/version-1.4.html +31 -3
  176. nautobot/project-static/docs/release-notes/version-1.5.html +30 -2
  177. nautobot/project-static/docs/release-notes/version-1.6.html +573 -134
  178. nautobot/project-static/docs/release-notes/version-2.0.html +30 -2
  179. nautobot/project-static/docs/release-notes/version-2.1.html +539 -170
  180. nautobot/project-static/docs/search/search_index.json +1 -1
  181. nautobot/project-static/docs/sitemap.xml +250 -240
  182. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  183. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +30 -2
  184. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +30 -2
  185. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +30 -2
  186. nautobot/project-static/docs/user-guide/administration/configuration/index.html +30 -2
  187. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +49 -2
  188. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +30 -2
  189. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +30 -2
  190. nautobot/project-static/docs/user-guide/administration/guides/caching.html +30 -2
  191. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +30 -2
  192. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +30 -2
  193. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +30 -2
  194. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +30 -2
  195. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +30 -2
  196. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +30 -2
  197. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +30 -2
  198. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +30 -2
  199. nautobot/project-static/docs/user-guide/administration/installation/docker.html +37 -5
  200. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +30 -2
  201. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +6019 -0
  202. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +30 -2
  203. nautobot/project-static/docs/user-guide/administration/installation/index.html +30 -2
  204. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +30 -2
  205. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +30 -2
  206. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +33 -5
  207. nautobot/project-static/docs/user-guide/administration/installation/services.html +30 -2
  208. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +30 -2
  209. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +30 -2
  210. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +30 -2
  211. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +30 -2
  212. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +30 -2
  213. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +30 -2
  214. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +30 -2
  215. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +30 -2
  216. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +30 -2
  217. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +30 -2
  218. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +30 -2
  219. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +30 -2
  220. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +30 -2
  221. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +30 -2
  222. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +30 -2
  223. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +30 -2
  224. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +30 -2
  225. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +30 -2
  226. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +30 -2
  227. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +30 -2
  228. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +30 -2
  229. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +30 -2
  230. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +30 -2
  231. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +30 -2
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +30 -2
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +30 -2
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +30 -2
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +30 -2
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +30 -2
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +30 -2
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +30 -2
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +30 -2
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +30 -2
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +30 -2
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +30 -2
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +30 -2
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +30 -2
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +30 -2
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +30 -2
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +30 -2
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +30 -2
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +30 -2
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +30 -2
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +30 -2
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +30 -2
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +30 -2
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +30 -2
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +30 -2
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +30 -2
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +30 -2
  258. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +30 -2
  259. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +30 -2
  260. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +30 -2
  261. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +30 -2
  262. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +30 -2
  263. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +30 -2
  264. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +30 -2
  265. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +30 -2
  266. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +30 -2
  267. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +30 -2
  268. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +30 -2
  269. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +30 -2
  270. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +30 -2
  271. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +30 -2
  272. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +30 -2
  273. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +30 -2
  274. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +30 -2
  275. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +30 -2
  276. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +30 -2
  277. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +33 -5
  278. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +30 -2
  279. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +30 -2
  280. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +30 -2
  281. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +30 -2
  282. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +30 -2
  283. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +30 -2
  284. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +30 -2
  285. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +30 -2
  286. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +30 -2
  287. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +30 -2
  288. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +30 -2
  289. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +30 -2
  290. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +30 -2
  291. nautobot/project-static/docs/user-guide/index.html +30 -2
  292. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +30 -2
  293. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +30 -2
  294. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +111 -15
  295. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +30 -2
  296. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +30 -2
  297. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +30 -2
  298. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +30 -2
  299. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +30 -2
  300. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +30 -2
  301. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +30 -2
  302. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +30 -2
  303. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +30 -2
  304. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +30 -2
  305. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +30 -2
  306. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +30 -2
  307. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +30 -2
  308. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +30 -2
  309. nautobot/project-static/docs/user-guide/platform-functionality/note.html +30 -2
  310. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +30 -2
  311. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +30 -2
  312. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +30 -2
  313. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +30 -2
  314. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +30 -2
  315. nautobot/project-static/docs/user-guide/platform-functionality/role.html +30 -2
  316. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +30 -2
  317. nautobot/project-static/docs/user-guide/platform-functionality/status.html +30 -2
  318. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +30 -2
  319. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +30 -2
  320. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +30 -2
  321. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +30 -2
  322. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +30 -2
  323. nautobot/tenancy/api/urls.py +1 -2
  324. nautobot/tenancy/api/views.py +0 -12
  325. nautobot/tenancy/navigation.py +1 -1
  326. nautobot/tenancy/tests/test_filters.py +0 -168
  327. nautobot/users/api/urls.py +1 -2
  328. nautobot/users/api/views.py +2 -65
  329. nautobot/users/views.py +8 -8
  330. nautobot/virtualization/api/urls.py +1 -2
  331. nautobot/virtualization/api/views.py +0 -12
  332. nautobot/virtualization/tests/test_filters.py +0 -28
  333. {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/METADATA +2 -2
  334. {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/RECORD +338 -334
  335. {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/LICENSE.txt +0 -0
  336. {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/NOTICE +0 -0
  337. {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/WHEEL +0 -0
  338. {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/entry_points.txt +0 -0
nautobot/apps/api.py CHANGED
@@ -19,7 +19,7 @@ from nautobot.core.api.fields import (
19
19
  )
20
20
  from nautobot.core.api.mixins import WritableSerializerMixin
21
21
  from nautobot.core.api.parsers import NautobotCSVParser
22
- from nautobot.core.api.routers import OrderedDefaultRouter
22
+ from nautobot.core.api.routers import AuthenticatedAPIRootView as APIRootView, OrderedDefaultRouter
23
23
  from nautobot.core.api.schema import NautobotAutoSchema
24
24
  from nautobot.core.api.serializers import (
25
25
  OptInFieldsMixin,
@@ -36,7 +36,6 @@ from nautobot.core.api.utils import (
36
36
  versioned_serializer_selector,
37
37
  )
38
38
  from nautobot.core.api.views import (
39
- APIRootView,
40
39
  BulkDestroyModelMixin,
41
40
  BulkUpdateModelMixin,
42
41
  GetObjectCountsView,
nautobot/apps/utils.py CHANGED
@@ -32,6 +32,8 @@ from nautobot.core.utils.lookup import (
32
32
  get_related_class_for_model,
33
33
  get_route_for_model,
34
34
  get_table_for_model,
35
+ get_url_for_url_pattern,
36
+ get_url_patterns,
35
37
  )
36
38
  from nautobot.core.utils.navigation import (
37
39
  get_all_new_ui_ready_routes,
@@ -110,6 +112,8 @@ __all__ = (
110
112
  "get_route_for_model",
111
113
  "get_settings_or_config",
112
114
  "get_table_for_model",
115
+ "get_url_for_url_pattern",
116
+ "get_url_patterns",
113
117
  "get_worker_count",
114
118
  "GitRepo",
115
119
  "hex_to_rgb",
nautobot/apps/views.py CHANGED
@@ -8,6 +8,7 @@ from nautobot.core.views.generic import (
8
8
  BulkImportView,
9
9
  BulkRenameView,
10
10
  ComponentCreateView,
11
+ GenericView,
11
12
  ObjectDeleteView,
12
13
  ObjectEditView,
13
14
  ObjectImportView,
@@ -57,6 +58,7 @@ __all__ = (
57
58
  "csv_format",
58
59
  "EnhancedPage",
59
60
  "EnhancedPaginator",
61
+ "GenericView",
60
62
  "get_csv_form_fields_from_serializer_class",
61
63
  "get_paginate_count",
62
64
  "GetReturnURLMixin",
@@ -2,8 +2,7 @@ from nautobot.core.api.routers import OrderedDefaultRouter
2
2
 
3
3
  from . import views
4
4
 
5
- router = OrderedDefaultRouter()
6
- router.APIRootView = views.CircuitsRootView
5
+ router = OrderedDefaultRouter(view_name="Circuits")
7
6
 
8
7
  # Providers
9
8
  router.register("providers", views.ProviderViewSet)
@@ -1,5 +1,3 @@
1
- from rest_framework.routers import APIRootView
2
-
3
1
  from nautobot.circuits import filters
4
2
  from nautobot.circuits.models import Circuit, CircuitTermination, CircuitType, Provider, ProviderNetwork
5
3
  from nautobot.core.models.querysets import count_related
@@ -8,16 +6,6 @@ from nautobot.extras.api.views import NautobotModelViewSet
8
6
 
9
7
  from . import serializers
10
8
 
11
-
12
- class CircuitsRootView(APIRootView):
13
- """
14
- Circuits API root view
15
- """
16
-
17
- def get_view_name(self):
18
- return "Circuits"
19
-
20
-
21
9
  #
22
10
  # Providers
23
11
  #
@@ -110,10 +110,6 @@ class CircuitRelationshipsTestCase(SeleniumTestCase):
110
110
  destination_type=fake_ct,
111
111
  destination_id=uuid.uuid4(),
112
112
  )
113
- # Clear the server-side cache of relationship information to make sure it gets refreshed properly
114
- Relationship.objects.get_for_model.cache_clear()
115
- Relationship.objects.get_for_model_source.cache_clear()
116
- Relationship.objects.get_for_model_destination.cache_clear()
117
113
 
118
114
  def tearDown(self):
119
115
  self.logout()
@@ -1,12 +1,23 @@
1
- from collections import OrderedDict
1
+ import logging
2
2
 
3
3
  from rest_framework.routers import DefaultRouter
4
4
 
5
+ from nautobot.core.api.views import AuthenticatedAPIRootView
6
+
7
+ logger = logging.getLogger(__name__)
8
+
5
9
 
6
10
  class OrderedDefaultRouter(DefaultRouter):
7
- def __init__(self, *args, **kwargs):
11
+ APIRootView = AuthenticatedAPIRootView
12
+
13
+ def __init__(self, *args, view_name=None, view_description=None, **kwargs):
8
14
  super().__init__(*args, **kwargs)
9
15
 
16
+ self.view_name = view_name
17
+ if view_name and not view_description:
18
+ view_description = f"{view_name} API root view"
19
+ self.view_description = view_description
20
+
10
21
  # Extend the list view mappings to support the DELETE operation
11
22
  self.routes[0].mapping.update(
12
23
  {
@@ -20,9 +31,20 @@ class OrderedDefaultRouter(DefaultRouter):
20
31
  """
21
32
  Wrap DRF's DefaultRouter to return an alphabetized list of endpoints.
22
33
  """
23
- api_root_dict = OrderedDict()
34
+ api_root_dict = {}
24
35
  list_name = self.routes[0].name
36
+
25
37
  for prefix, _viewset, basename in sorted(self.registry, key=lambda x: x[0]):
26
38
  api_root_dict[prefix] = list_name.format(basename=basename)
27
39
 
40
+ if issubclass(self.APIRootView, AuthenticatedAPIRootView):
41
+ return self.APIRootView.as_view(
42
+ api_root_dict=api_root_dict, name=self.view_name, description=self.view_description
43
+ )
44
+ # Fallback for the established practice of overriding self.APIRootView with a custom class
45
+ logger.warning(
46
+ "Something has changed an OrderedDefaultRouter's APIRootView attribute to a custom class. "
47
+ "Please verify that class %s implements appropriate authentication controls.",
48
+ self.APIRootView.__name__,
49
+ )
28
50
  return self.APIRootView.as_view(api_root_dict=api_root_dict)
@@ -178,6 +178,10 @@ def get_view_name(view, suffix=None):
178
178
 
179
179
  else:
180
180
  # Replicate DRF's built-in behavior.
181
+ name = getattr(view, "name", None)
182
+ if name is not None:
183
+ return view.name
184
+
181
185
  name = view.__class__.__name__
182
186
  name = formatting.remove_trailing_string(name, "View")
183
187
  name = formatting.remove_trailing_string(name, "ViewSet")
@@ -22,9 +22,9 @@ from graphql import get_default_backend
22
22
  from graphql.execution import ExecutionResult
23
23
  from graphql.execution.middleware import MiddlewareManager
24
24
  from graphql.type.schema import GraphQLSchema
25
- from rest_framework import status
25
+ from rest_framework import routers, status
26
26
  from rest_framework.exceptions import ParseError, PermissionDenied
27
- from rest_framework.permissions import AllowAny, IsAuthenticated
27
+ from rest_framework.permissions import IsAuthenticated
28
28
  from rest_framework.response import Response
29
29
  from rest_framework.reverse import reverse
30
30
  from rest_framework.views import APIView
@@ -341,15 +341,25 @@ class ReadOnlyModelViewSet(NautobotAPIVersionMixin, ModelViewSetMixin, ReadOnlyM
341
341
  #
342
342
 
343
343
 
344
- class APIRootView(NautobotAPIVersionMixin, APIView):
344
+ class AuthenticatedAPIRootView(NautobotAPIVersionMixin, routers.APIRootView):
345
345
  """
346
- This is the root of the REST API. API endpoints are arranged by app and model name; e.g. `/api/dcim/locations/`.
346
+ Extends DRF's base APIRootView class to enforce user authentication.
347
347
  """
348
348
 
349
- _ignore_model_permissions = True
349
+ permission_classes = [IsAuthenticated]
350
+
351
+ name = None
352
+ description = None
353
+
354
+
355
+ class APIRootView(AuthenticatedAPIRootView):
356
+ """
357
+ This is the root of the REST API.
350
358
 
351
- def get_view_name(self):
352
- return "API Root"
359
+ API endpoints are arranged by app and model name; e.g. `/api/dcim/locations/`.
360
+ """
361
+
362
+ name = "API Root"
353
363
 
354
364
  @extend_schema(exclude=True)
355
365
  def get(self, request, format=None): # pylint: disable=redefined-builtin
@@ -492,11 +502,6 @@ class NautobotSpectacularSwaggerView(APIVersioningGetSchemaURLMixin, Spectacular
492
502
  @extend_schema(exclude=True)
493
503
  def get(self, request, *args, **kwargs):
494
504
  """Fix up the rendering of the Swagger UI to work with Nautobot's UI."""
495
- if not request.user.is_authenticated:
496
- doc_url = reverse("api_docs")
497
- login_url = reverse(settings.LOGIN_URL)
498
- return redirect(f"{login_url}?next={doc_url}")
499
-
500
505
  # For backward compatibility wtih drf-yasg, `/api/docs/?format=openapi` is a redirect to the JSON schema.
501
506
  if request.GET.get("format") == "openapi":
502
507
  return redirect("schema_json", permanent=True)
@@ -525,11 +530,12 @@ class NautobotSpectacularRedocView(APIVersioningGetSchemaURLMixin, SpectacularRe
525
530
  class GraphQLDRFAPIView(NautobotAPIVersionMixin, APIView):
526
531
  """
527
532
  API View for GraphQL to integrate properly with DRF authentication mechanism.
528
- The code is a stripped down version of graphene-django default View
529
- https://github.com/graphql-python/graphene-django/blob/main/graphene_django/views.py#L57
530
533
  """
531
534
 
532
- permission_classes = [AllowAny]
535
+ # The code is a stripped down version of graphene-django default View
536
+ # https://github.com/graphql-python/graphene-django/blob/main/graphene_django/views.py#L57
537
+
538
+ permission_classes = [IsAuthenticated]
533
539
  graphql_schema = None
534
540
  executor = None
535
541
  backend = None
@@ -1,7 +1,9 @@
1
1
  from collections.abc import Mapping
2
2
  import logging
3
+ from pathlib import Path
3
4
 
4
5
  from celery import current_app
6
+ from django.conf import settings
5
7
  from django_celery_beat.schedulers import DatabaseScheduler, ModelEntry
6
8
  from kombu.utils.json import loads
7
9
 
@@ -94,3 +96,14 @@ class NautobotDatabaseScheduler(DatabaseScheduler):
94
96
  entry.total_run_count = entry.model.total_run_count
95
97
  entry.model.save()
96
98
  return resp
99
+
100
+ def tick(self, *args, **kwargs):
101
+ """
102
+ Run a tick - one iteration of the scheduler.
103
+
104
+ This is an extension of `celery.beat.Scheduler.tick()` to touch the `CELERY_BEAT_HEARTBEAT_FILE` file.
105
+ """
106
+ interval = super().tick(*args, **kwargs)
107
+ if settings.CELERY_BEAT_HEARTBEAT_FILE:
108
+ Path(settings.CELERY_BEAT_HEARTBEAT_FILE).touch(exist_ok=True)
109
+ return interval
nautobot/core/choices.py CHANGED
@@ -11,27 +11,6 @@ class ChoiceSetMeta(type):
11
11
  choices = getattr(cls, "CHOICES", ())
12
12
  return iter(choices)
13
13
 
14
- def __getattribute__(cls, attr):
15
- """
16
- Overrides the default __getattribute__ method to provide custom behavior when accessing attributes of a ChoiceSet
17
- instance. If the attribute name is an uppercase string and not equal to 'CHOICES', this method returns a new class
18
- instance of the same type as the original attribute value, but with an additional 'label' property. This 'label'
19
- property looks up the choice label for the given attribute value in the 'CHOICES' sequence and returns it.
20
- """
21
- value = super().__getattribute__(attr)
22
- # Check if the attribute is a member of the CHOICES sequence and is uppercase;
23
- # Mostly only ChoiceSet choices are uppercase
24
- if attr != "CHOICES" and attr.isupper():
25
- choices = cls.as_dict()
26
-
27
- class Choice(value.__class__):
28
- @property
29
- def label(self):
30
- return choices.get(self)
31
-
32
- return Choice(value)
33
- return value
34
-
35
14
 
36
15
  class ChoiceSet(metaclass=ChoiceSetMeta):
37
16
  """
@@ -94,7 +94,7 @@ class BaseModel(models.Model):
94
94
 
95
95
  Necessary for use with _content_type_cached and management commands.
96
96
  """
97
- return f"{cls._meta.label_lower}._content_type"
97
+ return f"nautobot.{cls._meta.label_lower}._content_type"
98
98
 
99
99
  @classproperty # https://github.com/PyCQA/pylint-django/issues/240
100
100
  def _content_type_cached(cls): # pylint: disable=no-self-argument
@@ -1,5 +1,3 @@
1
- from functools import cached_property
2
-
3
1
  from django.core.cache import cache
4
2
  from django.db.models import Case, When
5
3
  from tree_queries.models import TreeNode
@@ -41,8 +39,20 @@ class TreeQuerySet(TreeQuerySet_, querysets.RestrictedQuerySet):
41
39
  return model_class.objects.without_tree_fields().filter(pk__in=ancestor_pks).order_by(preserve_order)
42
40
 
43
41
  def max_tree_depth(self):
44
- """
45
- Get the maximum depth of any tree in this queryset.
42
+ r"""
43
+ Get the maximum tree depth of any node in this queryset.
44
+
45
+ In most cases you should use TreeManager.max_depth instead as it's cached and this is not.
46
+
47
+ root - depth 0
48
+ \
49
+ branch - depth 1
50
+ \
51
+ leaf - depth 2
52
+
53
+ Note that a queryset with only root nodes will return zero, and an empty queryset will also return zero.
54
+ This is probably a bug, we should really return -1 in the case of an empty queryset, but this is
55
+ "working as implemented" and changing it would possibly be a breaking change at this point.
46
56
  """
47
57
  deepest = self.with_tree_fields().extra(order_by=["-__tree.tree_depth"]).first()
48
58
  if deepest is not None:
@@ -58,9 +68,21 @@ class TreeManager(TreeManager_, BaseManager.from_queryset(TreeQuerySet)):
58
68
  _with_tree_fields = True
59
69
  use_in_migrations = True
60
70
 
61
- @cached_property
71
+ @property
72
+ def max_depth_cache_key(self):
73
+ return f"nautobot.{self.model._meta.concrete_model._meta.label_lower}.max_depth"
74
+
75
+ @property
62
76
  def max_depth(self):
63
- return self.max_tree_depth()
77
+ """Cacheable version of `TreeQuerySet.max_tree_depth()`.
78
+
79
+ Generally TreeManagers are persistent objects while TreeQuerySets are not, hence the difference in behavior.
80
+ """
81
+ max_depth = cache.get(self.max_depth_cache_key)
82
+ if max_depth is None:
83
+ max_depth = self.max_tree_depth()
84
+ cache.set(self.max_depth_cache_key, max_depth)
85
+ return max_depth
64
86
 
65
87
 
66
88
  class TreeModel(TreeNode):
@@ -82,7 +104,7 @@ class TreeModel(TreeNode):
82
104
  """
83
105
  if not hasattr(self, "name"):
84
106
  raise NotImplementedError("default TreeModel.display implementation requires a `name` attribute!")
85
- cache_key = f"{self.__class__.__name__}.{self.id}.display"
107
+ cache_key = f"nautobot.{self._meta.concrete_model._meta.label_lower}.{self.id}.display"
86
108
  display_str = cache.get(cache_key, "")
87
109
  if display_str:
88
110
  return display_str
nautobot/core/releases.py CHANGED
@@ -18,7 +18,7 @@ def get_latest_release(pre_releases=False):
18
18
  """
19
19
  if get_settings_or_config("RELEASE_CHECK_URL"):
20
20
  logger.debug("Checking for most recent release")
21
- latest_release = cache.get("latest_release")
21
+ latest_release = cache.get("nautobot.core.releases.get_latest_release")
22
22
  if latest_release is not None:
23
23
  logger.debug(f"Found cached release: {latest_release}")
24
24
  return latest_release
nautobot/core/settings.py CHANGED
@@ -1,7 +1,9 @@
1
1
  import os
2
+ import os.path
2
3
  import platform
3
4
  import re
4
5
  import sys
6
+ import tempfile
5
7
 
6
8
  from django.contrib.messages import constants as messages
7
9
  import django.forms
@@ -240,6 +242,7 @@ SPECTACULAR_SETTINGS = {
240
242
  # trim it from all of the individual paths correspondingly.
241
243
  # See also https://github.com/nautobot/nautobot-ansible/pull/135 for an example of why this is desirable.
242
244
  "SERVERS": [{"url": "/api"}],
245
+ "SERVE_PERMISSIONS": ["rest_framework.permissions.IsAuthenticated"],
243
246
  "SCHEMA_PATH_PREFIX": "/api",
244
247
  "SCHEMA_PATH_PREFIX_TRIM": True,
245
248
  # use sidecar - locally packaged UI files, not CDN
@@ -767,6 +770,12 @@ CONTENT_TYPE_CACHE_TIMEOUT = int(os.getenv("NAUTOBOT_CONTENT_TYPE_CACHE_TIMEOUT"
767
770
  # Celery (used for background processing)
768
771
  #
769
772
 
773
+ # Celery Beat heartbeat file path - will be touched by Beat each time it wakes up as a proof-of-health.
774
+ CELERY_BEAT_HEARTBEAT_FILE = os.getenv(
775
+ "NAUTOBOT_CELERY_BEAT_HEARTBEAT_FILE",
776
+ os.path.join(tempfile.gettempdir(), "nautobot_celery_beat_heartbeat"),
777
+ )
778
+
770
779
  # Celery broker URL used to tell workers where queues are located
771
780
  CELERY_BROKER_URL = os.getenv("NAUTOBOT_CELERY_BROKER_URL", parse_redis_connection(redis_database=0))
772
781
 
@@ -1,7 +1,6 @@
1
1
  """Helper functions to detect settings after app initialization (AKA 'dynamic settings')."""
2
2
 
3
3
  from collections import namedtuple
4
- from functools import lru_cache
5
4
  import os
6
5
 
7
6
  from django.conf import settings
@@ -10,26 +9,14 @@ ConstanceConfigItem = namedtuple("ConstanceConfigItem", ["default", "help_text",
10
9
 
11
10
  #
12
11
  # X_auth_enabled checks to see if a backend has been specified, thus assuming it is enabled.
13
- # Leverages `lru_cache` since these are called per user session. The wrappers are a
14
- # workaround to pass `lru_cache` a hashable data structure.
15
12
  #
16
13
 
17
14
 
18
15
  def remote_auth_enabled(auth_backends):
19
- return _remote_auth_enabled(tuple(auth_backends))
20
-
21
-
22
- @lru_cache(maxsize=5)
23
- def _remote_auth_enabled(auth_backends):
24
16
  return "nautobot.core.authentication.RemoteUserBackend" in auth_backends
25
17
 
26
18
 
27
19
  def sso_auth_enabled(auth_backends):
28
- return _sso_auth_enabled(tuple(auth_backends))
29
-
30
-
31
- @lru_cache(maxsize=5)
32
- def _sso_auth_enabled(auth_backends):
33
20
  for backend in auth_backends:
34
21
  if backend.startswith(settings.SOCIAL_AUTH_BACKEND_PREFIX):
35
22
  return True
@@ -37,11 +24,6 @@ def _sso_auth_enabled(auth_backends):
37
24
 
38
25
 
39
26
  def ldap_auth_enabled(auth_backends):
40
- return _ldap_auth_enabled(tuple(auth_backends))
41
-
42
-
43
- @lru_cache(maxsize=5)
44
- def _ldap_auth_enabled(auth_backends):
45
27
  return "django_auth_ldap.backend.LDAPBackend" in auth_backends
46
28
 
47
29
 
nautobot/core/signals.py CHANGED
@@ -1,11 +1,14 @@
1
1
  """Custom signals and handlers for the core Nautobot application."""
2
+ import contextlib
2
3
  from functools import wraps
3
4
  import inspect
4
5
  import logging
5
6
 
6
7
  from django.contrib.auth.signals import user_logged_in, user_logged_out
8
+ from django.core.cache import cache
7
9
  from django.db.models.signals import post_delete, post_save
8
10
  from django.dispatch import receiver, Signal
11
+ import redis.exceptions
9
12
 
10
13
  nautobot_database_ready = Signal()
11
14
  """
@@ -67,8 +70,5 @@ def invalidate_max_depth_cache(sender, **kwargs):
67
70
  if not isinstance(sender.objects, TreeManager):
68
71
  return
69
72
 
70
- # Note that we can't use `hasattr`, because this will trigger `max_depth` to be calculated if it doesn't exist
71
- try:
72
- del sender.objects.max_depth
73
- except AttributeError:
74
- pass
73
+ with contextlib.suppress(redis.exceptions.ConnectionError):
74
+ cache.delete(sender.objects.max_depth_cache_key)
nautobot/core/tasks.py CHANGED
@@ -21,7 +21,7 @@ def get_releases(pre_releases=False):
21
21
  releases = []
22
22
 
23
23
  # Check whether this URL has failed recently and shouldn't be retried yet
24
- if url == cache.get("latest_release_no_retry"):
24
+ if url == cache.get("nautobot.core.releases.get_releases.no_retry"):
25
25
  logger.info(f"Skipping release check; URL failed recently: {url}")
26
26
  return []
27
27
 
@@ -42,11 +42,15 @@ def get_releases(pre_releases=False):
42
42
  except requests.exceptions.RequestException:
43
43
  # The request failed. Set a flag in the cache to disable future checks to this URL for 15 minutes.
44
44
  logger.exception(f"Error while fetching {url}. Disabling checks for 15 minutes.")
45
- cache.set("latest_release_no_retry", url, 900)
45
+ cache.set("nautobot.core.releases.get_releases.no_retry", url, 900)
46
46
  return []
47
47
 
48
48
  # Cache the most recent release
49
- cache.set("latest_release", max(releases), config.get_settings_or_config("RELEASE_CHECK_TIMEOUT"))
49
+ cache.set(
50
+ "nautobot.core.releases.get_latest_release",
51
+ max(releases),
52
+ config.get_settings_or_config("RELEASE_CHECK_TIMEOUT"),
53
+ )
50
54
 
51
55
  # Since this is a Celery task, we can't return Version objects as they are not JSON serializable.
52
56
  return [(str(version), url) for version, url in releases]
@@ -8,55 +8,7 @@
8
8
  <html lang="en"{% if request.COOKIES|get_item:"theme" == 'dark' %} data-theme="dark"{% endif %}>
9
9
  <head>
10
10
  <title>{% block title %}Admin Home{% endblock %} - Nautobot</title>
11
- <link rel="stylesheet"
12
- href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}"
13
- onerror="window.location='{% url 'media_failure' %}?filename=bootstrap-3.4.1-dist/css/bootstrap.min.css'">
14
- <link rel="stylesheet"
15
- href="{% static 'materialdesignicons-6.5.95/css/materialdesignicons.min.css' %}"
16
- onerror="window.location='{% url 'media_failure' %}?filename=materialdesignicons-6.5.95/css/materialdesignicons.min.css'">
17
- <link rel="stylesheet"
18
- href="{% static 'jquery-ui-1.13.1/jquery-ui.min.css' %}"
19
- onerror="window.location='{% url 'media_failure' %}?filename=jquery-ui-1.13.1/jquery-ui.min.css'">
20
- <link rel="stylesheet"
21
- href="{% static 'select2-4.0.13/select2.min.css' %}"
22
- onerror="window.location='{% url 'media_failure' %}?filename=select2-4.0.13/select2.min.css'">
23
- <link rel="stylesheet"
24
- href="{% static 'select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css' %}"
25
- onerror="window.location='{% url 'media_failure' %}?filename=select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css'">
26
- <link rel="stylesheet"
27
- href="{% static 'flatpickr-4.6.9/themes/light.min.css' %}"
28
- onerror="window.location='{% url 'media_failure' %}?filename=flatpickr-4.6.9/themes/light.min.css'">
29
- <link rel="stylesheet" id="dark-theme"
30
- href="{% versioned_static 'css/dark.css' %}"
31
- onerror="window.location='{% url 'media_failure' %}?filename=css/dark.css'"{% if request.COOKIES|get_item:"theme" != 'dark' %} disabled="disabled"{% endif %} />
32
- <link rel="stylesheet" id="base-theme"
33
- href="{% versioned_static 'css/base.css' %}"
34
- onerror="window.location='{% url 'media_failure' %}?filename=css/base.css'">
35
- <link rel="apple-touch-icon" sizes="180x180" href="{% custom_branding_or_static 'icon_180' 'img/nautobot_icon_180x180.png' %}">
36
- <link rel="icon" type="image/png" sizes="32x32" href="{% custom_branding_or_static 'icon_32' 'img/nautobot_icon_32x32.png' %}">
37
- <link rel="icon" type="image/png" sizes="16x16" href="{% custom_branding_or_static 'icon_16' 'img/nautobot_icon_16x16.png' %}">
38
- <link rel="icon" type="image/png" href="{% custom_branding_or_static 'icon_192' 'img/nautobot_icon_192x192.png' %}" sizes="192x192">
39
- <link rel="mask-icon" href="{% custom_branding_or_static 'icon_mask' 'img/nautobot_icon_monochrome.svg' %}" color="#0097ff">
40
- <link rel="shortcut icon" href="{% custom_branding_or_static 'favicon' 'img/favicon.ico' %}">
41
-
42
- <script src="{% static 'jquery/jquery-3.6.0.min.js' %}"
43
- onerror="window.location='{% url 'media_failure' %}?filename=jquery/jquery-3.6.0.min.js'"></script>
44
- <script src="{% static 'jquery-ui-1.13.1/jquery-ui.min.js' %}"
45
- onerror="window.location='{% url 'media_failure' %}?filename=jquery-ui-1.13.1/jquery-ui.min.js'"></script>
46
- <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"
47
- onerror="window.location='{% url 'media_failure' %}?filename=bootstrap-3.4.1-dist/js/bootstrap.min.js'"></script>
48
- <script src="{% static 'select2-4.0.13/select2.min.js' %}"
49
- onerror="window.location='{% url 'media_failure' %}?filename=select2-4.0.13/select2.min.js'"></script>
50
- <script src="{% static 'flatpickr-4.6.9/flatpickr.min.js' %}"
51
- onerror="window.location='{% url 'media_failure' %}?filename=flatpickr-4.6.9/flatpickr.min.js'"></script>
52
- <script src="{% static 'js/forms.js' %}"
53
- onerror="window.location='{% url 'media_failure' %}?filename=js/forms.js'"></script>
54
-
55
- <meta name="msapplication-TileColor" content="#2d89ef">
56
- <meta name="theme-color" content="#ffffff">
57
- <meta charset="UTF-8">
58
- <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
59
-
11
+ {% include 'inc/media.html' %}
60
12
  {% block extrahead %}{% endblock %}
61
13
  {% block extrastyle %}{% endblock %}
62
14
 
@@ -94,23 +46,27 @@
94
46
  }
95
47
  </style>
96
48
  </head>
97
- <body {% if is_popup %}style="padding-top: 0px;"{% endif %}>
49
+ <body {% if is_popup %}style="padding-left: 0px;"{% endif %}>
50
+
98
51
  {% if not is_popup %}
99
52
  {% include 'inc/nav_menu.html' %}
100
53
  {% endif %}
101
- <div class="container-fluid wrapper" {% if is_popup %}style="padding-bottom: 0px;"{% endif %}>
102
- {% if "BANNER_TOP"|settings_or_config %}
103
- <div class="alert alert-info text-center" role="alert">
104
- {{ "BANNER_TOP"|settings_or_config|safe }}
105
- </div>
106
- {% endif %}
107
- {% if settings.MAINTENANCE_MODE %}
108
- <div class="alert alert-warning text-center" role="alert">
109
- <h4><i class="mdi mdi-alert"></i> Maintenance Mode</h4>
110
- <p>Nautobot is currently in maintenance mode. Functionality may be limited.</p>
111
- </div>
54
+
55
+ <div class="container-fluid wrapper" id="main-content" {% if is_popup %}style="padding-bottom: 0px;"{% endif %}>
56
+ {% if not is_popup %}
57
+ {% if "BANNER_TOP"|settings_or_config %}
58
+ <div class="alert alert-info text-center" role="alert">
59
+ {{ "BANNER_TOP"|settings_or_config|safe }}
60
+ </div>
61
+ {% endif %}
62
+ {% if settings.MAINTENANCE_MODE %}
63
+ <div class="alert alert-warning text-center" role="alert">
64
+ <h4><i class="mdi mdi-alert"></i> Maintenance Mode</h4>
65
+ <p>Nautobot is currently in maintenance mode. Functionality may be limited.</p>
66
+ </div>
67
+ {% endif %}
68
+ {% plugin_banners %}
112
69
  {% endif %}
113
- {% plugin_banners %}
114
70
  {% for message in messages %}
115
71
  <div class="alert alert-{{ message.tags }} alert-dismissable" role="alert">
116
72
  <button type="button" class="close" data-dismiss="alert" aria-label="Close">
@@ -148,8 +104,6 @@
148
104
  {% block content %}{{ content }}{% endblock %}
149
105
  </div>
150
106
  </div>
151
-
152
- {% block footer %}<footer id="footer"></footer>{% endblock %}
153
107
  <div class="push"></div>
154
108
  {% if "BANNER_BOTTOM"|settings_or_config %}
155
109
  <div class="alert alert-info text-center banner-bottom" role="alert">
@@ -158,37 +112,12 @@
158
112
  {% endif %}
159
113
  </div>
160
114
  {% if not is_popup %}
161
- <footer class="footer">
162
- <div class="container-fluid">
163
- <div class="row">
164
- <div class="col-xs-4">
165
- <p class="text-muted">{{ settings.HOSTNAME }} (v{{ settings.VERSION }})</p>
166
- </div>
167
- <div class="col-xs-4 text-center">
168
- <p class="text-muted">{% now 'Y-m-d H:i:s T' %}</p>
169
- </div>
170
- <div class="col-xs-4 text-right noprint">
171
- <p class="text-muted">
172
- <a href="#theme_modal" data-toggle="modal" data-target="#theme_modal" id="btn-theme-modal"><i class="mdi mdi-theme-light-dark text-primary"></i>Theme</a> &middot;
173
- <i class="mdi mdi-book-open-page-variant text-primary"></i>
174
- {% if settings.BRANDING_URLS.docs %}
175
- <a href="{{ settings.BRANDING_URLS.docs }}">Docs</a>
176
- {% else %}
177
- <a href="{% static 'docs/index.html' %}">Docs</a>
178
- {% endif %}
179
- &middot;
180
- <i class="mdi mdi-cloud-braces text-primary"></i> <a href="{% url 'api_docs' %}">API</a> &middot;
181
- <i class="mdi mdi-graphql text-primary"></i> <a href="{% url 'graphql' %}">GraphQL</a> &middot;
182
- <i class="mdi mdi-xml text-primary"></i> <a href="{{ settings.BRANDING_URLS.code }}">Code</a> &middot;
183
- <i class="mdi mdi-lifebuoy text-primary"></i> <a href="{{ settings.BRANDING_URLS.help }}">Help</a>
184
- </p>
185
- </div>
186
- </div>
187
- </div>
188
- </footer>
115
+ {% include 'inc/footer.html' %}
189
116
  {% endif %}
190
117
  {% include 'modals/modal_theme.html' with name='theme'%}
191
- <script src="{% versioned_static 'js/theme.js' %}"></script>
192
- {% block javascript %}{% endblock %}
118
+
119
+ {% block javascript %}
120
+ {% include 'inc/javascript.html' %}
121
+ {% endblock %}
193
122
  </body>
194
123
  </html>