nautobot 2.2.0b1__py3-none-any.whl → 2.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (425) hide show
  1. nautobot/__init__.py +31 -0
  2. nautobot/apps/api.py +1 -2
  3. nautobot/apps/utils.py +4 -0
  4. nautobot/apps/views.py +2 -0
  5. nautobot/circuits/api/urls.py +1 -2
  6. nautobot/circuits/api/views.py +0 -12
  7. nautobot/circuits/apps.py +1 -1
  8. nautobot/circuits/tests/test_filters.py +1 -1
  9. nautobot/core/api/routers.py +50 -3
  10. nautobot/core/api/utils.py +4 -0
  11. nautobot/core/api/views.py +21 -15
  12. nautobot/core/cli/__init__.py +18 -11
  13. nautobot/core/constants.py +85 -0
  14. nautobot/core/filters.py +7 -1
  15. nautobot/core/forms/widgets.py +1 -2
  16. nautobot/core/graphql/schema.py +1 -0
  17. nautobot/core/management/commands/generate_test_data.py +4 -4
  18. nautobot/core/models/__init__.py +1 -0
  19. nautobot/core/settings.py +24 -3
  20. nautobot/core/settings.yaml +20 -0
  21. nautobot/core/signals.py +1 -0
  22. nautobot/core/tables.py +2 -1
  23. nautobot/core/templates/admin/base.html +23 -94
  24. nautobot/core/templates/generic/object_retrieve.html +2 -2
  25. nautobot/core/templates/graphene/graphiql.html +18 -47
  26. nautobot/core/templates/inc/footer.html +5 -5
  27. nautobot/core/templates/inc/javascript.html +4 -4
  28. nautobot/core/templates/inc/media.html +2 -2
  29. nautobot/core/templates/inc/nav_menu.html +0 -7
  30. nautobot/core/templates/nautobot_config.py.j2 +14 -1
  31. nautobot/core/templates/rest_framework/api.html +12 -5
  32. nautobot/core/templatetags/helpers.py +2 -2
  33. nautobot/core/testing/__init__.py +1 -1
  34. nautobot/core/testing/filters.py +1 -1
  35. nautobot/core/testing/views.py +30 -0
  36. nautobot/core/tests/integration/test_view_authentication.py +68 -0
  37. nautobot/core/tests/test_api.py +13 -6
  38. nautobot/core/tests/test_csv.py +5 -4
  39. nautobot/core/tests/test_filters.py +2 -1
  40. nautobot/core/tests/test_graphql.py +4 -14
  41. nautobot/core/tests/test_navigations.py +3 -0
  42. nautobot/core/tests/test_views.py +44 -16
  43. nautobot/core/utils/data.py +1 -2
  44. nautobot/core/utils/lookup.py +126 -0
  45. nautobot/core/views/__init__.py +3 -7
  46. nautobot/core/views/generic.py +20 -6
  47. nautobot/core/views/mixins.py +7 -1
  48. nautobot/core/views/renderers.py +11 -6
  49. nautobot/dcim/api/serializers.py +4 -4
  50. nautobot/dcim/api/urls.py +2 -3
  51. nautobot/dcim/api/views.py +7 -18
  52. nautobot/dcim/apps.py +8 -4
  53. nautobot/dcim/elevations.py +5 -1
  54. nautobot/dcim/factory.py +7 -7
  55. nautobot/dcim/filters/__init__.py +16 -17
  56. nautobot/dcim/forms.py +61 -45
  57. nautobot/dcim/homepage.py +11 -3
  58. nautobot/dcim/management/commands/migrate_location_contacts.py +218 -0
  59. nautobot/dcim/migrations/0057_controller_models.py +11 -70
  60. nautobot/dcim/models/__init__.py +2 -2
  61. nautobot/dcim/models/devices.py +14 -16
  62. nautobot/dcim/models/racks.py +1 -3
  63. nautobot/dcim/navigation.py +23 -31
  64. nautobot/dcim/signals.py +6 -6
  65. nautobot/dcim/tables/__init__.py +2 -2
  66. nautobot/dcim/tables/devices.py +13 -16
  67. nautobot/dcim/tables/template_code.py +1 -1
  68. nautobot/dcim/templates/dcim/controller_create.html +70 -0
  69. nautobot/dcim/templates/dcim/controller_retrieve.html +35 -18
  70. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +88 -0
  71. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +75 -43
  72. nautobot/dcim/templates/dcim/device.html +11 -3
  73. nautobot/dcim/templates/dcim/device_edit.html +1 -1
  74. nautobot/dcim/templates/dcim/devicefamily_retrieve.html +4 -0
  75. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +1 -1
  76. nautobot/dcim/tests/test_api.py +47 -6
  77. nautobot/dcim/tests/test_filters.py +92 -81
  78. nautobot/dcim/tests/test_graphql.py +11 -1
  79. nautobot/dcim/tests/test_models.py +15 -15
  80. nautobot/dcim/tests/test_signals.py +3 -1
  81. nautobot/dcim/tests/test_views.py +24 -12
  82. nautobot/dcim/urls.py +1 -1
  83. nautobot/dcim/views.py +25 -15
  84. nautobot/extras/api/serializers.py +20 -1
  85. nautobot/extras/api/urls.py +1 -2
  86. nautobot/extras/api/views.py +0 -10
  87. nautobot/extras/apps.py +7 -0
  88. nautobot/extras/context_managers.py +15 -4
  89. nautobot/extras/filters/__init__.py +53 -2
  90. nautobot/extras/filters/customfields.py +14 -9
  91. nautobot/extras/filters/mixins.py +6 -1
  92. nautobot/extras/forms/contacts.py +7 -0
  93. nautobot/extras/health_checks.py +1 -0
  94. nautobot/extras/jobs.py +1 -0
  95. nautobot/extras/managers.py +15 -2
  96. nautobot/extras/models/contacts.py +1 -0
  97. nautobot/extras/models/customfields.py +25 -2
  98. nautobot/extras/models/datasources.py +1 -0
  99. nautobot/extras/models/mixins.py +1 -0
  100. nautobot/extras/navigation.py +71 -65
  101. nautobot/extras/plugins/__init__.py +2 -1
  102. nautobot/extras/plugins/views.py +7 -11
  103. nautobot/extras/querysets.py +1 -2
  104. nautobot/extras/secrets/providers.py +1 -0
  105. nautobot/extras/signals.py +15 -5
  106. nautobot/extras/tasks.py +70 -17
  107. nautobot/extras/tests/test_api.py +2 -4
  108. nautobot/extras/tests/test_customfields.py +72 -9
  109. nautobot/extras/tests/test_dynamicgroups.py +2 -0
  110. nautobot/extras/tests/test_filters.py +89 -4
  111. nautobot/extras/tests/test_models.py +9 -0
  112. nautobot/extras/tests/test_relationships.py +10 -1
  113. nautobot/extras/tests/test_views.py +112 -1
  114. nautobot/extras/views.py +18 -17
  115. nautobot/ipam/api/serializers.py +10 -0
  116. nautobot/ipam/api/urls.py +1 -2
  117. nautobot/ipam/api/views.py +0 -11
  118. nautobot/ipam/apps.py +3 -2
  119. nautobot/ipam/tables.py +2 -22
  120. nautobot/ipam/tests/test_graphql.py +2 -3
  121. nautobot/ipam/tests/test_tables.py +42 -0
  122. nautobot/ipam/tests/test_views.py +1 -0
  123. nautobot/ipam/views.py +9 -9
  124. nautobot/project-static/css/base.css +1 -0
  125. nautobot/project-static/docs/404.html +126 -73
  126. nautobot/project-static/docs/apps/index.html +127 -71
  127. nautobot/project-static/docs/apps/nautobot-apps.html +127 -71
  128. nautobot/project-static/docs/assets/javascripts/{bundle.8fd75fb4.min.js → bundle.bd41221c.min.js} +2 -2
  129. nautobot/project-static/docs/assets/javascripts/{bundle.8fd75fb4.min.js.map → bundle.bd41221c.min.js.map} +3 -3
  130. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +1 -0
  131. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +1 -0
  132. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +127 -71
  133. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +127 -71
  134. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +167 -73
  135. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +165 -72
  136. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +127 -71
  137. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +127 -71
  138. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +127 -71
  139. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +127 -71
  140. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +127 -71
  141. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +127 -71
  142. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +127 -71
  143. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +127 -71
  144. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +127 -71
  145. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +127 -71
  146. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +127 -71
  147. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +127 -71
  148. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +127 -71
  149. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +127 -71
  150. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +128 -72
  151. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +127 -71
  152. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +127 -71
  153. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +345 -71
  154. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +172 -73
  155. nautobot/project-static/docs/development/apps/api/configuration-view.html +127 -71
  156. nautobot/project-static/docs/development/apps/api/database-backend-config.html +127 -71
  157. nautobot/project-static/docs/development/apps/api/models/django-admin.html +127 -71
  158. nautobot/project-static/docs/development/apps/api/models/global-search.html +127 -71
  159. nautobot/project-static/docs/development/apps/api/models/graphql.html +127 -71
  160. nautobot/project-static/docs/development/apps/api/models/index.html +127 -71
  161. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +127 -71
  162. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +127 -71
  163. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +127 -71
  164. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +127 -71
  165. nautobot/project-static/docs/development/apps/api/platform-features/index.html +127 -71
  166. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +127 -71
  167. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +127 -71
  168. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +127 -71
  169. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +127 -71
  170. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +127 -71
  171. nautobot/project-static/docs/development/apps/api/prometheus.html +127 -71
  172. nautobot/project-static/docs/development/apps/api/setup.html +127 -71
  173. nautobot/project-static/docs/development/apps/api/testing.html +127 -71
  174. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +127 -71
  175. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +127 -71
  176. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +127 -71
  177. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +127 -71
  178. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +127 -71
  179. nautobot/project-static/docs/development/apps/api/views/base-template.html +127 -71
  180. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +141 -80
  181. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +144 -83
  182. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +127 -71
  183. nautobot/project-static/docs/development/apps/api/views/index.html +127 -71
  184. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +127 -71
  185. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +127 -71
  186. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +127 -71
  187. nautobot/project-static/docs/development/apps/api/views/notes.html +127 -71
  188. nautobot/project-static/docs/development/apps/api/views/rest-api.html +127 -71
  189. nautobot/project-static/docs/development/apps/api/views/urls.html +127 -71
  190. nautobot/project-static/docs/development/apps/index.html +127 -71
  191. nautobot/project-static/docs/development/apps/migration/code-updates.html +127 -71
  192. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +127 -71
  193. nautobot/project-static/docs/development/apps/migration/from-v1.html +127 -71
  194. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +127 -71
  195. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +127 -71
  196. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +127 -71
  197. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +127 -71
  198. nautobot/project-static/docs/development/apps/porting-from-netbox.html +127 -71
  199. nautobot/project-static/docs/development/core/application-registry.html +127 -71
  200. nautobot/project-static/docs/development/core/best-practices.html +145 -79
  201. nautobot/project-static/docs/development/core/bootstrap-ui.html +127 -71
  202. nautobot/project-static/docs/development/core/caching.html +127 -71
  203. nautobot/project-static/docs/development/core/controllers.html +141 -275
  204. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +127 -71
  205. nautobot/project-static/docs/development/core/extending-models.html +13 -8166
  206. nautobot/project-static/docs/development/core/generic-views.html +142 -86
  207. nautobot/project-static/docs/development/core/getting-started.html +146 -81
  208. nautobot/project-static/docs/development/core/homepage.html +145 -89
  209. nautobot/project-static/docs/development/core/index.html +127 -71
  210. nautobot/project-static/docs/development/core/model-checklist.html +8354 -0
  211. nautobot/project-static/docs/development/core/model-features.html +130 -74
  212. nautobot/project-static/docs/development/core/natural-keys.html +127 -71
  213. nautobot/project-static/docs/development/core/navigation-menu.html +127 -71
  214. nautobot/project-static/docs/development/core/release-checklist.html +127 -71
  215. nautobot/project-static/docs/development/core/role-internals.html +127 -71
  216. nautobot/project-static/docs/development/core/settings.html +127 -71
  217. nautobot/project-static/docs/development/core/style-guide.html +127 -71
  218. nautobot/project-static/docs/development/core/templates.html +127 -71
  219. nautobot/project-static/docs/development/core/testing.html +127 -71
  220. nautobot/project-static/docs/development/core/user-preferences.html +127 -71
  221. nautobot/project-static/docs/development/extending-models.html +3 -3
  222. nautobot/project-static/docs/development/index.html +127 -71
  223. nautobot/project-static/docs/development/jobs/index.html +128 -72
  224. nautobot/project-static/docs/development/jobs/migration/from-v1.html +127 -71
  225. nautobot/project-static/docs/index.html +126 -73
  226. nautobot/project-static/docs/models/dcim/{controllerdevicegroup.html → controllermanageddevicegroup.html} +3 -3
  227. nautobot/project-static/docs/objects.inv +0 -0
  228. nautobot/project-static/docs/release-notes/index.html +127 -71
  229. nautobot/project-static/docs/release-notes/version-1.0.html +127 -71
  230. nautobot/project-static/docs/release-notes/version-1.1.html +127 -71
  231. nautobot/project-static/docs/release-notes/version-1.2.html +127 -71
  232. nautobot/project-static/docs/release-notes/version-1.3.html +127 -71
  233. nautobot/project-static/docs/release-notes/version-1.4.html +127 -71
  234. nautobot/project-static/docs/release-notes/version-1.5.html +127 -71
  235. nautobot/project-static/docs/release-notes/version-1.6.html +127 -71
  236. nautobot/project-static/docs/release-notes/version-2.0.html +127 -71
  237. nautobot/project-static/docs/release-notes/version-2.1.html +538 -254
  238. nautobot/project-static/docs/release-notes/version-2.2.html +520 -101
  239. nautobot/project-static/docs/requirements.txt +3 -3
  240. nautobot/project-static/docs/search/search_index.json +1 -1
  241. nautobot/project-static/docs/sitemap.xml +264 -259
  242. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  243. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +127 -71
  244. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +127 -71
  245. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +127 -71
  246. nautobot/project-static/docs/user-guide/administration/configuration/index.html +127 -71
  247. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +192 -71
  248. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +127 -71
  249. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +127 -71
  250. nautobot/project-static/docs/user-guide/administration/guides/caching.html +127 -71
  251. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +127 -71
  252. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +127 -71
  253. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +127 -71
  254. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +131 -71
  255. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +127 -71
  256. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +127 -71
  257. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +130 -74
  258. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +127 -71
  259. nautobot/project-static/docs/user-guide/administration/installation/docker.html +134 -74
  260. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +127 -71
  261. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +8616 -0
  262. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +127 -71
  263. nautobot/project-static/docs/user-guide/administration/installation/index.html +127 -71
  264. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +127 -71
  265. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +127 -71
  266. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +130 -74
  267. nautobot/project-static/docs/user-guide/administration/installation/services.html +127 -71
  268. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +127 -71
  269. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +127 -71
  270. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +127 -71
  271. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +127 -71
  272. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +127 -71
  273. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +127 -71
  274. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +127 -71
  275. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +127 -71
  276. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +127 -71
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +127 -71
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +127 -71
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +127 -71
  280. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +127 -71
  281. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +127 -71
  282. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +127 -71
  283. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +127 -71
  284. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +127 -71
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +127 -71
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +127 -71
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +127 -71
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +127 -71
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +127 -71
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +127 -71
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +362 -79
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/{controllerdevicegroup.html → controllermanageddevicegroup.html} +210 -85
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +127 -71
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +127 -71
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +127 -71
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +127 -71
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +127 -71
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +127 -71
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +127 -71
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +127 -71
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +127 -71
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +127 -71
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +127 -71
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +127 -71
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +127 -71
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +127 -71
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +127 -71
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +127 -71
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +127 -71
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +127 -71
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +127 -71
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +127 -71
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +127 -71
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +127 -71
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +127 -71
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +127 -71
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +127 -71
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +127 -71
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +127 -71
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +127 -71
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +127 -71
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +127 -71
  323. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +130 -74
  324. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +127 -71
  325. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +138 -71
  326. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +138 -71
  327. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +127 -71
  328. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +127 -71
  329. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +127 -71
  330. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +127 -71
  331. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +127 -71
  332. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +127 -71
  333. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +127 -71
  334. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +127 -71
  335. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +127 -71
  336. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +127 -71
  337. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +127 -71
  338. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +127 -71
  339. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +127 -71
  340. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +127 -71
  341. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +127 -71
  342. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +127 -71
  343. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +127 -71
  344. nautobot/project-static/docs/user-guide/feature-guides/{contact-and-team.html → contacts-and-teams.html} +128 -72
  345. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +129 -73
  346. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +127 -71
  347. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +127 -71
  348. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +127 -71
  349. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +127 -71
  350. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +127 -71
  351. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +127 -71
  352. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +129 -73
  353. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +127 -71
  354. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +127 -71
  355. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +127 -71
  356. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +127 -71
  357. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +127 -71
  358. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +127 -71
  359. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +127 -71
  360. nautobot/project-static/docs/user-guide/index.html +127 -71
  361. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +127 -71
  362. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +127 -71
  363. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +127 -71
  364. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +127 -71
  365. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +127 -71
  366. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +127 -71
  367. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +127 -71
  368. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +127 -71
  369. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +127 -71
  370. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +127 -71
  371. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +127 -71
  372. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +127 -71
  373. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +127 -71
  374. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +127 -71
  375. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +127 -71
  376. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +127 -71
  377. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +127 -71
  378. nautobot/project-static/docs/user-guide/platform-functionality/note.html +127 -71
  379. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +127 -71
  380. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +127 -71
  381. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +127 -71
  382. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +127 -71
  383. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +127 -71
  384. nautobot/project-static/docs/user-guide/platform-functionality/role.html +127 -71
  385. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +127 -71
  386. nautobot/project-static/docs/user-guide/platform-functionality/status.html +127 -71
  387. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +127 -71
  388. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +127 -71
  389. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +127 -71
  390. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +127 -71
  391. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +127 -71
  392. nautobot/project-static/jquery/jquery-3.7.1.min.js +2 -0
  393. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_444444_256x240.png +0 -0
  394. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_555555_256x240.png +0 -0
  395. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_777620_256x240.png +0 -0
  396. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_777777_256x240.png +0 -0
  397. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_cc0000_256x240.png +0 -0
  398. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_ffffff_256x240.png +0 -0
  399. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +7 -0
  400. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +6 -0
  401. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +5 -0
  402. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/jquery-ui.theme.min.css +1 -1
  403. nautobot/tenancy/api/urls.py +1 -2
  404. nautobot/tenancy/api/views.py +0 -12
  405. nautobot/tenancy/tables.py +1 -1
  406. nautobot/tenancy/tests/test_views.py +1 -0
  407. nautobot/users/api/urls.py +1 -2
  408. nautobot/users/api/views.py +2 -65
  409. nautobot/users/views.py +8 -8
  410. nautobot/virtualization/api/urls.py +1 -2
  411. nautobot/virtualization/api/views.py +0 -12
  412. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/METADATA +24 -24
  413. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/RECORD +418 -412
  414. nautobot/dcim/templates/dcim/controllerdevicegroup_create.html +0 -43
  415. nautobot/project-static/docs/assets/stylesheets/main.f2e4d321.min.css +0 -1
  416. nautobot/project-static/docs/assets/stylesheets/main.f2e4d321.min.css.map +0 -1
  417. nautobot/project-static/jquery/jquery-3.6.0.min.js +0 -2
  418. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.min.css +0 -7
  419. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.min.js +0 -6
  420. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.structure.min.css +0 -5
  421. /nautobot/dcim/templates/dcim/{controllerdevicegroup_retrieve.html → controllermanageddevicegroup_retrieve.html} +0 -0
  422. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/LICENSE.txt +0 -0
  423. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/NOTICE +0 -0
  424. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/WHEEL +0 -0
  425. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/entry_points.txt +0 -0
nautobot/__init__.py CHANGED
@@ -1,4 +1,35 @@
1
1
  from importlib import metadata
2
+ import logging
3
+ import os
4
+
5
+ import django
2
6
 
3
7
  # Primary package version
4
8
  __version__ = metadata.version(__name__)
9
+
10
+ # Sentinel to make sure we only initialize once.
11
+ __initialized = False
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def setup(config_path=None):
17
+ """Similar to `django.setup()`, this configures Django with the appropriate Nautobot settings data."""
18
+ from nautobot.core.cli import get_config_path, load_settings
19
+
20
+ global __initialized
21
+
22
+ if __initialized:
23
+ return
24
+
25
+ if config_path is None:
26
+ config_path = get_config_path()
27
+
28
+ # Point Django to our 'nautobot_config' pseudo-module that we'll load from the provided config path
29
+ os.environ["DJANGO_SETTINGS_MODULE"] = "nautobot_config"
30
+
31
+ load_settings(config_path)
32
+ django.setup()
33
+
34
+ logger.info("Nautobot initialized!")
35
+ __initialized = True
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
  get_view_for_model,
36
38
  )
37
39
  from nautobot.core.utils.migrations import migrate_content_type_references_to_new_model
@@ -113,6 +115,8 @@ __all__ = (
113
115
  "get_settings_or_config",
114
116
  "get_table_for_model",
115
117
  "get_view_for_model",
118
+ "get_url_for_url_pattern",
119
+ "get_url_patterns",
116
120
  "get_worker_count",
117
121
  "GitRepo",
118
122
  "hex_to_rgb",
nautobot/apps/views.py CHANGED
@@ -8,6 +8,7 @@ from nautobot.core.views.generic import (
8
8
  BulkImportView, # 3.0 TODO: deprecated, will be removed in 3.0
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
  #
nautobot/circuits/apps.py CHANGED
@@ -5,8 +5,8 @@ class CircuitsConfig(NautobotConfig):
5
5
  name = "nautobot.circuits"
6
6
  verbose_name = "Circuits"
7
7
  searchable_models = [
8
- "provider",
9
8
  "circuit",
9
+ "provider",
10
10
  "providernetwork",
11
11
  ]
12
12
 
@@ -57,7 +57,7 @@ class ProviderTestCase(FilterTestCases.NameOnlyFilterTestCase, FilterTestCases.F
57
57
  def test_location(self):
58
58
  expected = self.queryset.filter(
59
59
  circuits__circuit_terminations__location__in=[self.locations[0].pk, self.locations[1].pk]
60
- )
60
+ ).distinct()
61
61
  params = {"location": [self.locations[0].pk, self.locations[1].pk]}
62
62
  self.assertQuerysetEqualAndNotEmpty(self.filterset(params, self.queryset).qs, expected)
63
63
  params = {"location": [self.locations[0].name, self.locations[1].name]}
@@ -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
  {
@@ -16,13 +27,49 @@ class OrderedDefaultRouter(DefaultRouter):
16
27
  }
17
28
  )
18
29
 
30
+ def register(self, prefix, viewset, basename=None):
31
+ """
32
+ Override DRF's BaseRouter.register() to bypass an unnecessary restriction added in version 3.15.0.
33
+
34
+ (Reference: https://github.com/encode/django-rest-framework/pull/8438)
35
+ """
36
+ if basename is None:
37
+ basename = self.get_default_basename(viewset)
38
+
39
+ # DRF:
40
+ # if self.is_already_registered(basename):
41
+ # msg = (f'Router with basename "{basename}" is already registered. '
42
+ # f'Please provide a unique basename for viewset "{viewset}"')
43
+ # raise ImproperlyConfigured(msg)
44
+ #
45
+ # We bypass this because we have at least one use case (/api/extras/jobs/) where we are *intentionally*
46
+ # registering two viewsets with the same basename, but have carefully defined them so as not to conflict.
47
+
48
+ # resuming standard DRF code...
49
+ self.registry.append((prefix, viewset, basename))
50
+
51
+ # invalidate the urls cache
52
+ if hasattr(self, "_urls"):
53
+ del self._urls
54
+
19
55
  def get_api_root_view(self, api_urls=None):
20
56
  """
21
57
  Wrap DRF's DefaultRouter to return an alphabetized list of endpoints.
22
58
  """
23
- api_root_dict = OrderedDict()
59
+ api_root_dict = {}
24
60
  list_name = self.routes[0].name
61
+
25
62
  for prefix, _viewset, basename in sorted(self.registry, key=lambda x: x[0]):
26
63
  api_root_dict[prefix] = list_name.format(basename=basename)
27
64
 
65
+ if issubclass(self.APIRootView, AuthenticatedAPIRootView):
66
+ return self.APIRootView.as_view(
67
+ api_root_dict=api_root_dict, name=self.view_name, description=self.view_description
68
+ )
69
+ # Fallback for the established practice of overriding self.APIRootView with a custom class
70
+ logger.warning(
71
+ "Something has changed an OrderedDefaultRouter's APIRootView attribute to a custom class. "
72
+ "Please verify that class %s implements appropriate authentication controls.",
73
+ self.APIRootView.__name__,
74
+ )
28
75
  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")
@@ -24,9 +24,9 @@ from graphql import get_default_backend
24
24
  from graphql.execution import ExecutionResult
25
25
  from graphql.execution.middleware import MiddlewareManager
26
26
  from graphql.type.schema import GraphQLSchema
27
- from rest_framework import status
27
+ from rest_framework import routers, status
28
28
  from rest_framework.exceptions import ParseError, PermissionDenied
29
- from rest_framework.permissions import AllowAny, IsAuthenticated
29
+ from rest_framework.permissions import IsAuthenticated
30
30
  from rest_framework.response import Response
31
31
  from rest_framework.reverse import reverse
32
32
  from rest_framework.views import APIView
@@ -347,15 +347,25 @@ class ReadOnlyModelViewSet(NautobotAPIVersionMixin, ModelViewSetMixin, ReadOnlyM
347
347
  #
348
348
 
349
349
 
350
- class APIRootView(NautobotAPIVersionMixin, APIView):
350
+ class AuthenticatedAPIRootView(NautobotAPIVersionMixin, routers.APIRootView):
351
351
  """
352
- This is the root of the REST API. API endpoints are arranged by app and model name; e.g. `/api/dcim/locations/`.
352
+ Extends DRF's base APIRootView class to enforce user authentication.
353
353
  """
354
354
 
355
- _ignore_model_permissions = True
355
+ permission_classes = [IsAuthenticated]
356
+
357
+ name = None
358
+ description = None
359
+
360
+
361
+ class APIRootView(AuthenticatedAPIRootView):
362
+ """
363
+ This is the root of the REST API.
356
364
 
357
- def get_view_name(self):
358
- return "API Root"
365
+ API endpoints are arranged by app and model name; e.g. `/api/dcim/locations/`.
366
+ """
367
+
368
+ name = "API Root"
359
369
 
360
370
  @extend_schema(exclude=True)
361
371
  def get(self, request, format=None): # pylint: disable=redefined-builtin
@@ -504,11 +514,6 @@ class NautobotSpectacularSwaggerView(APIVersioningGetSchemaURLMixin, Spectacular
504
514
  @extend_schema(exclude=True)
505
515
  def get(self, request, *args, **kwargs):
506
516
  """Fix up the rendering of the Swagger UI to work with Nautobot's UI."""
507
- if not request.user.is_authenticated:
508
- doc_url = reverse("api_docs")
509
- login_url = reverse(settings.LOGIN_URL)
510
- return redirect(f"{login_url}?next={doc_url}")
511
-
512
517
  # For backward compatibility wtih drf-yasg, `/api/docs/?format=openapi` is a redirect to the JSON schema.
513
518
  if request.GET.get("format") == "openapi":
514
519
  return redirect("schema_json", permanent=True)
@@ -537,11 +542,12 @@ class NautobotSpectacularRedocView(APIVersioningGetSchemaURLMixin, SpectacularRe
537
542
  class GraphQLDRFAPIView(NautobotAPIVersionMixin, APIView):
538
543
  """
539
544
  API View for GraphQL to integrate properly with DRF authentication mechanism.
540
- The code is a stripped down version of graphene-django default View
541
- https://github.com/graphql-python/graphene-django/blob/main/graphene_django/views.py#L57
542
545
  """
543
546
 
544
- permission_classes = [AllowAny]
547
+ # The code is a stripped down version of graphene-django default View
548
+ # https://github.com/graphql-python/graphene-django/blob/main/graphene_django/views.py#L57
549
+
550
+ permission_classes = [IsAuthenticated]
545
551
  graphql_schema = None
546
552
  executor = None
547
553
  backend = None
@@ -13,7 +13,6 @@ from django.core.management import CommandError, CommandParser, execute_from_com
13
13
  from django.core.management.utils import get_random_secret_key
14
14
  from jinja2 import BaseLoader, Environment
15
15
 
16
- from nautobot import __version__
17
16
  from nautobot.core.settings_funcs import is_truthy
18
17
  from nautobot.extras.plugins.utils import load_plugins
19
18
 
@@ -126,7 +125,7 @@ def _preprocess_settings(settings, config_path):
126
125
  load_plugins(settings)
127
126
 
128
127
 
129
- def _load_settings(config_path):
128
+ def load_settings(config_path):
130
129
  """Load nautobot_config.py or its equivalent into memory as a `nautobot_config` pseudo-module."""
131
130
  if not os.path.exists(config_path):
132
131
  raise FileNotFoundError(
@@ -154,6 +153,8 @@ class _VersionAction(argparse.Action):
154
153
  super().__init__(*args, **kwargs)
155
154
 
156
155
  def __call__(self, parser, namespace, values, option_string):
156
+ from nautobot import __version__
157
+
157
158
  print(f"Nautobot version: {__version__}")
158
159
  print(f"Django version: {django.__version__}")
159
160
  print(f"Configuration file: {namespace.config_path}")
@@ -215,13 +216,9 @@ def _init_settings(args):
215
216
  print(f"Configuration file created at {config_path}")
216
217
 
217
218
 
218
- def main():
219
- """Run administrative tasks."""
220
- # Point Django to our 'nautobot_config' pseudo-module that we'll load from the provided config path
221
- os.environ["DJANGO_SETTINGS_MODULE"] = "nautobot_config"
222
-
223
- # Default config path based on NAUTOBOT_CONFIG or NAUTOBOT_ROOT environment variables
224
- config_path = os.getenv(
219
+ def get_config_path():
220
+ """Get the default Nautobot config file path based on the NAUTOBOT_CONFIG or NAUTOBOT_ROOT environment variables."""
221
+ return os.getenv(
225
222
  "NAUTOBOT_CONFIG",
226
223
  os.path.join(
227
224
  os.getenv("NAUTOBOT_ROOT", os.path.expanduser("~/.nautobot")),
@@ -229,13 +226,23 @@ def main():
229
226
  ),
230
227
  )
231
228
 
229
+
230
+ def main():
231
+ """Run administrative tasks."""
232
+ # Point Django to our 'nautobot_config' pseudo-module that we'll load from the provided config path
233
+ os.environ["DJANGO_SETTINGS_MODULE"] = "nautobot_config"
234
+
235
+ default_config_path = get_config_path()
236
+
232
237
  # Intercept certain CLI parameters and arguments before they reach Django
233
238
  parser = CommandParser(
234
239
  description=DESCRIPTION,
235
240
  usage=USAGE,
236
241
  formatter_class=_VerboseHelpFormatter,
237
242
  )
238
- parser.add_argument("-c", "--config-path", default=config_path, help="Path to the Nautobot configuration file")
243
+ parser.add_argument(
244
+ "-c", "--config-path", default=default_config_path, help="Path to the Nautobot configuration file"
245
+ )
239
246
  parser.add_argument("--version", action=_VersionAction, help="Show version numbers and exit")
240
247
 
241
248
  # Parse out the `--config` argument here and capture the rest of the CLI args
@@ -282,7 +289,7 @@ def main():
282
289
  raise
283
290
 
284
291
  # If we get here, it's a regular Django management command - so load in the nautobot_config.py then hand off
285
- _load_settings(args.config_path)
292
+ load_settings(args.config_path)
286
293
  execute_from_command_line([sys.argv[0], *unparsed_args])
287
294
 
288
295
 
@@ -104,4 +104,89 @@ COMPOSITE_KEY_SEPARATOR = ";"
104
104
  # For the natural slug separator, it's much simpler and we can just go with "_".
105
105
  NATURAL_SLUG_SEPARATOR = "_"
106
106
 
107
+ # For config settings that contain a list of things.
108
+ # As environment variables only allow string types, these need to be split into the final list.
109
+ CONFIG_SETTING_SEPARATOR = ","
110
+
107
111
  CHARFIELD_MAX_LENGTH = 255
112
+
113
+ # Models excluded from the global search list
114
+ GLOBAL_SEARCH_EXCLUDE_LIST = [
115
+ "anotherexamplemodel",
116
+ "cablepath",
117
+ "circuittermination",
118
+ "circuittype",
119
+ "clustergroup",
120
+ "clustertype",
121
+ "computedfield",
122
+ "configcontext",
123
+ "configcontextschema",
124
+ "consoleport",
125
+ "consoleporttemplate",
126
+ "consoleserverport",
127
+ "consoleserverporttemplate",
128
+ "contactassociation",
129
+ "controllermanageddevicegroup",
130
+ "customfield",
131
+ "customfieldchoice",
132
+ "customlink",
133
+ "devicebay",
134
+ "devicebaytemplate",
135
+ "devicetypetosoftwareimagefile",
136
+ "dynamicgroupmembership",
137
+ "exporttemplate",
138
+ "fileattachment",
139
+ "fileproxy",
140
+ "frontport",
141
+ "frontporttemplate",
142
+ "graphqlquery",
143
+ "healthchecktestmodel",
144
+ "imageattachment",
145
+ "interface",
146
+ "interfaceredundancygroup",
147
+ "interfaceredundancygroupassociation",
148
+ "interfacetemplate",
149
+ "inventoryitem",
150
+ "ipaddresstointerface",
151
+ "job",
152
+ "jobbutton",
153
+ "jobhook",
154
+ "joblogentry",
155
+ "jobresult",
156
+ "locationtype",
157
+ "manufacturer",
158
+ "note",
159
+ "objectchange",
160
+ "platform",
161
+ "poweroutlet",
162
+ "poweroutlettemplate",
163
+ "powerpanel",
164
+ "powerport",
165
+ "powerporttemplate",
166
+ "prefixlocationassignment",
167
+ "rackreservation",
168
+ "rearport",
169
+ "rearporttemplate",
170
+ "relationship",
171
+ "relationshipassociation",
172
+ "rir",
173
+ "role",
174
+ "routetarget",
175
+ "scheduledjob",
176
+ "scheduledjobs",
177
+ "secret",
178
+ "secretsgroup",
179
+ "secretsgroupassociation",
180
+ "service",
181
+ "softwareimagefile",
182
+ "status",
183
+ "tag",
184
+ "taggeditem",
185
+ "tenantgroup",
186
+ "vlangroup",
187
+ "vlanlocationassignment",
188
+ "vminterface",
189
+ "vrfdeviceassignment",
190
+ "vrfprefixassignment",
191
+ "webhook",
192
+ ]
nautobot/core/filters.py CHANGED
@@ -260,6 +260,9 @@ class ContentTypeMultipleChoiceFilter(django_filters.MultipleChoiceFilter):
260
260
  if not self.conjoined:
261
261
  qs = qs.filter(q)
262
262
 
263
+ if self.distinct:
264
+ qs = qs.distinct()
265
+
263
266
  return qs
264
267
 
265
268
 
@@ -522,7 +525,10 @@ class TreeNodeMultipleChoiceFilter(NaturalKeyOrPKMultipleChoiceFilter):
522
525
 
523
526
  # Fetch the generated Q object and filter the incoming qs with it before passing it along.
524
527
  query = self.generate_query(value)
525
- return self.get_method(qs)(query)
528
+ result = self.get_method(qs)(query)
529
+ if self.distinct:
530
+ result = result.distinct()
531
+ return result
526
532
 
527
533
 
528
534
  #
@@ -182,8 +182,7 @@ class APISelect(SelectWithDisabled):
182
182
  # ModelChoiceIterator.__iter__() yields a tuple of (value, label)
183
183
  # using this approach first yield a tuple of (null(value), null_option(label))
184
184
  yield "null", self.null_options
185
- for item in super().__iter__():
186
- yield item
185
+ yield from super().__iter__()
187
186
 
188
187
  null_option = self.attrs.get("data-null-option")
189
188
  self.choices = ModelChoiceIteratorWithNullOption(field=self.choices.field, null_option=null_option)
@@ -1,4 +1,5 @@
1
1
  """Schema module for GraphQL."""
2
+
2
3
  from collections import OrderedDict
3
4
  import logging
4
5
 
@@ -58,8 +58,8 @@ class Command(BaseCommand):
58
58
  ProviderNetworkFactory,
59
59
  )
60
60
  from nautobot.dcim.factory import (
61
- ControllerDeviceGroupFactory,
62
61
  ControllerFactory,
62
+ ControllerManagedDeviceGroupFactory,
63
63
  DeviceFactory,
64
64
  DeviceFamilyFactory,
65
65
  DeviceRedundancyGroupFactory,
@@ -131,7 +131,7 @@ class Command(BaseCommand):
131
131
  LocationFactory.create_batch(10, has_parent=False, using=db_name)
132
132
  self.stdout.write("Creating Controller with Groups...")
133
133
  ControllerFactory.create_batch(1)
134
- ControllerDeviceGroupFactory.create_batch(5)
134
+ ControllerManagedDeviceGroupFactory.create_batch(5)
135
135
  self.stdout.write("Creating RIRs...")
136
136
  RIRFactory.create_batch(9, using=db_name) # only 9 unique RIR names are hard-coded presently
137
137
  self.stdout.write("Creating RouteTargets...")
@@ -207,7 +207,7 @@ class Command(BaseCommand):
207
207
  ExternalIntegrationFactory.create_batch(20, using=db_name)
208
208
  self.stdout.write("Creating Controllers with Device or DeviceRedundancyGroups...")
209
209
  ControllerFactory.create_batch(10)
210
- ControllerDeviceGroupFactory.create_batch(30)
210
+ ControllerManagedDeviceGroupFactory.create_batch(30)
211
211
  # make sure we have some tenants that have null relationships to make filter tests happy
212
212
  self.stdout.write("Creating Tenants without Circuits, Locations, IPAddresses, or Prefixes...")
213
213
  TenantFactory.create_batch(10, using=db_name)
@@ -227,7 +227,7 @@ class Command(BaseCommand):
227
227
  CircuitTerminationFactory,
228
228
  CircuitTypeFactory,
229
229
  ContactFactory,
230
- ControllerDeviceGroupFactory,
230
+ ControllerManagedDeviceGroupFactory,
231
231
  ControllerFactory,
232
232
  DeviceFactory,
233
233
  DeviceFamilyFactory,
@@ -55,6 +55,7 @@ class BaseModel(models.Model):
55
55
  )
56
56
 
57
57
  objects = BaseManager.from_queryset(RestrictedQuerySet)()
58
+ is_contact_associable_model = True
58
59
 
59
60
  class Meta:
60
61
  abstract = True
nautobot/core/settings.py CHANGED
@@ -10,6 +10,7 @@ import django.forms
10
10
  from django.utils.safestring import mark_safe
11
11
 
12
12
  from nautobot import __version__
13
+ from nautobot.core.constants import CONFIG_SETTING_SEPARATOR as _CONFIG_SETTING_SEPARATOR
13
14
  from nautobot.core.settings_funcs import ConstanceConfigItem, is_truthy, parse_redis_connection
14
15
 
15
16
  #
@@ -152,6 +153,9 @@ if "NAUTOBOT_MAX_PAGE_SIZE" in os.environ and os.environ["NAUTOBOT_MAX_PAGE_SIZE
152
153
  # Metrics
153
154
  METRICS_ENABLED = is_truthy(os.getenv("NAUTOBOT_METRICS_ENABLED", "False"))
154
155
  METRICS_AUTHENTICATED = is_truthy(os.getenv("NAUTOBOT_METRICS_AUTHENTICATED", "False"))
156
+ METRICS_DISABLED_APPS = []
157
+ if "NAUTOBOT_METRICS_DISABLED_APPS" in os.environ and os.environ["NAUTOBOT_METRICS_DISABLED_APPS"] != "":
158
+ METRICS_DISABLED_APPS = os.getenv("NAUTOBOT_METRICS_DISABLED_APPS", "").split(_CONFIG_SETTING_SEPARATOR)
155
159
 
156
160
  # Napalm
157
161
  NAPALM_ARGS = {}
@@ -166,7 +170,7 @@ if "NAUTOBOT_PAGINATE_COUNT" in os.environ and os.environ["NAUTOBOT_PAGINATE_COU
166
170
  # The options displayed in the web interface dropdown to limit the number of objects per page.
167
171
  # Default is [25, 50, 100, 250, 500, 1000]
168
172
  if "NAUTOBOT_PER_PAGE_DEFAULTS" in os.environ and os.environ["NAUTOBOT_PER_PAGE_DEFAULTS"] != "":
169
- PER_PAGE_DEFAULTS = [int(val) for val in os.environ["NAUTOBOT_PER_PAGE_DEFAULTS"].split(",")]
173
+ PER_PAGE_DEFAULTS = [int(val) for val in os.environ["NAUTOBOT_PER_PAGE_DEFAULTS"].split(_CONFIG_SETTING_SEPARATOR)]
170
174
 
171
175
  # Plugins
172
176
  PLUGINS = []
@@ -188,6 +192,13 @@ if (
188
192
  ):
189
193
  RACK_ELEVATION_DEFAULT_UNIT_WIDTH = int(os.environ["NAUTOBOT_RACK_ELEVATION_DEFAULT_UNIT_WIDTH"])
190
194
 
195
+ # Enable two-digit format for the rack unit numbering in rack elevations.
196
+ if (
197
+ "NAUTOBOT_RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT" in os.environ
198
+ and os.environ["NAUTOBOT_RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT"] != ""
199
+ ):
200
+ RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT = is_truthy(os.environ["NAUTOBOT_RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT"])
201
+
191
202
  # How frequently to check for a new Nautobot release on GitHub, and the URL to check for this information.
192
203
  # Defaults to disabled (no URL) and check every 24 hours when enabled
193
204
  if "NAUTOBOT_RELEASE_CHECK_TIMEOUT" in os.environ and os.environ["NAUTOBOT_RELEASE_CHECK_TIMEOUT"] != "":
@@ -332,6 +343,7 @@ SPECTACULAR_SETTINGS = {
332
343
  # trim it from all of the individual paths correspondingly.
333
344
  # See also https://github.com/nautobot/nautobot-ansible/pull/135 for an example of why this is desirable.
334
345
  "SERVERS": [{"url": "/api"}],
346
+ "SERVE_PERMISSIONS": ["rest_framework.permissions.IsAuthenticated"],
335
347
  "SCHEMA_PATH_PREFIX": "/api",
336
348
  "SCHEMA_PATH_PREFIX_TRIM": True,
337
349
  # use sidecar - locally packaged UI files, not CDN
@@ -768,6 +780,11 @@ CONSTANCE_CONFIG = {
768
780
  "RACK_ELEVATION_DEFAULT_UNIT_WIDTH": ConstanceConfigItem(
769
781
  default=230, help_text="Default width (in pixels) of a rack unit in a rack elevation diagram", field_type=int
770
782
  ),
783
+ "RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT": ConstanceConfigItem(
784
+ default=False,
785
+ help_text="Enables two-digit format for the rack unit numbering in a rack elevation diagram",
786
+ field_type=bool,
787
+ ),
771
788
  "RELEASE_CHECK_TIMEOUT": ConstanceConfigItem(
772
789
  default=24 * 3600,
773
790
  help_text="Number of seconds (must be at least 3600, or one hour) to cache the result of a release check "
@@ -799,7 +816,11 @@ CONSTANCE_CONFIG_FIELDSETS = {
799
816
  "Natural Keys": ["DEVICE_NAME_AS_NATURAL_KEY", "LOCATION_NAME_AS_NATURAL_KEY"],
800
817
  "Pagination": ["PAGINATE_COUNT", "MAX_PAGE_SIZE", "PER_PAGE_DEFAULTS"],
801
818
  "Performance": ["DYNAMIC_GROUPS_MEMBER_CACHE_TIMEOUT", "JOB_CREATE_FILE_MAX_SIZE"],
802
- "Rack Elevation Rendering": ["RACK_ELEVATION_DEFAULT_UNIT_HEIGHT", "RACK_ELEVATION_DEFAULT_UNIT_WIDTH"],
819
+ "Rack Elevation Rendering": [
820
+ "RACK_ELEVATION_DEFAULT_UNIT_HEIGHT",
821
+ "RACK_ELEVATION_DEFAULT_UNIT_WIDTH",
822
+ "RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT",
823
+ ],
803
824
  "Release Checking": ["RELEASE_CHECK_URL", "RELEASE_CHECK_TIMEOUT"],
804
825
  "User Interface": ["SUPPORT_MESSAGE"],
805
826
  "Debugging": ["ALLOW_REQUEST_PROFILING"],
@@ -913,7 +934,7 @@ CELERY_TASK_TIME_LIMIT = int(os.getenv("NAUTOBOT_CELERY_TASK_TIME_LIMIT", str(10
913
934
  CELERY_WORKER_PROMETHEUS_PORTS = []
914
935
  if os.getenv("NAUTOBOT_CELERY_WORKER_PROMETHEUS_PORTS"):
915
936
  CELERY_WORKER_PROMETHEUS_PORTS = [
916
- int(value) for value in os.getenv("NAUTOBOT_CELERY_WORKER_PROMETHEUS_PORTS").split(",")
937
+ int(value) for value in os.getenv("NAUTOBOT_CELERY_WORKER_PROMETHEUS_PORTS").split(_CONFIG_SETTING_SEPARATOR)
917
938
  ]
918
939
 
919
940
  # These settings define the custom nautobot serialization encoding as an accepted data encoding format