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

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

Potentially problematic release.


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

Files changed (362) hide show
  1. nautobot/apps/api.py +2 -1
  2. nautobot/apps/utils.py +0 -4
  3. nautobot/apps/views.py +0 -2
  4. nautobot/circuits/api/urls.py +2 -1
  5. nautobot/circuits/api/views.py +12 -0
  6. nautobot/circuits/tests/test_filters.py +1 -1
  7. nautobot/core/api/routers.py +3 -25
  8. nautobot/core/api/utils.py +0 -4
  9. nautobot/core/api/views.py +15 -21
  10. nautobot/core/filters.py +1 -7
  11. nautobot/core/management/commands/generate_test_data.py +4 -4
  12. nautobot/core/settings.py +0 -1
  13. nautobot/core/tables.py +1 -2
  14. nautobot/core/templates/admin/base.html +94 -23
  15. nautobot/core/templates/graphene/graphiql.html +47 -18
  16. nautobot/core/templates/inc/footer.html +5 -5
  17. nautobot/core/templates/inc/nav_menu.html +7 -0
  18. nautobot/core/templates/rest_framework/api.html +5 -12
  19. nautobot/core/templatetags/helpers.py +2 -2
  20. nautobot/core/testing/views.py +0 -30
  21. nautobot/core/tests/test_api.py +6 -13
  22. nautobot/core/tests/test_csv.py +4 -5
  23. nautobot/core/tests/test_filters.py +1 -2
  24. nautobot/core/tests/test_graphql.py +14 -4
  25. nautobot/core/tests/test_navigations.py +0 -3
  26. nautobot/core/tests/test_views.py +16 -22
  27. nautobot/core/utils/lookup.py +0 -124
  28. nautobot/core/views/__init__.py +7 -3
  29. nautobot/core/views/generic.py +3 -19
  30. nautobot/core/views/mixins.py +0 -7
  31. nautobot/core/views/renderers.py +1 -4
  32. nautobot/dcim/api/serializers.py +4 -4
  33. nautobot/dcim/api/urls.py +3 -2
  34. nautobot/dcim/api/views.py +18 -7
  35. nautobot/dcim/factory.py +7 -7
  36. nautobot/dcim/filters/__init__.py +17 -16
  37. nautobot/dcim/forms.py +45 -61
  38. nautobot/dcim/homepage.py +3 -11
  39. nautobot/dcim/migrations/0057_controller_models.py +70 -11
  40. nautobot/dcim/models/__init__.py +2 -2
  41. nautobot/dcim/models/devices.py +16 -14
  42. nautobot/dcim/models/racks.py +3 -1
  43. nautobot/dcim/navigation.py +31 -23
  44. nautobot/dcim/signals.py +6 -6
  45. nautobot/dcim/tables/__init__.py +2 -2
  46. nautobot/dcim/tables/devices.py +15 -12
  47. nautobot/dcim/tables/template_code.py +1 -1
  48. nautobot/dcim/templates/dcim/controller_retrieve.html +18 -35
  49. nautobot/dcim/templates/dcim/controllerdevicegroup_create.html +43 -0
  50. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +43 -67
  51. nautobot/dcim/templates/dcim/device.html +2 -10
  52. nautobot/dcim/templates/dcim/device_edit.html +1 -1
  53. nautobot/dcim/tests/test_api.py +6 -11
  54. nautobot/dcim/tests/test_filters.py +81 -92
  55. nautobot/dcim/tests/test_graphql.py +1 -11
  56. nautobot/dcim/tests/test_models.py +15 -15
  57. nautobot/dcim/tests/test_signals.py +0 -2
  58. nautobot/dcim/tests/test_views.py +12 -24
  59. nautobot/dcim/urls.py +1 -1
  60. nautobot/dcim/views.py +15 -19
  61. nautobot/extras/api/urls.py +2 -1
  62. nautobot/extras/api/views.py +10 -0
  63. nautobot/extras/filters/__init__.py +2 -53
  64. nautobot/extras/forms/contacts.py +0 -7
  65. nautobot/extras/managers.py +0 -14
  66. nautobot/extras/navigation.py +65 -71
  67. nautobot/extras/plugins/views.py +11 -7
  68. nautobot/extras/tests/test_api.py +0 -2
  69. nautobot/extras/tests/test_dynamicgroups.py +0 -2
  70. nautobot/extras/tests/test_filters.py +4 -89
  71. nautobot/extras/tests/test_models.py +0 -9
  72. nautobot/extras/tests/test_relationships.py +1 -10
  73. nautobot/extras/tests/test_views.py +1 -112
  74. nautobot/extras/views.py +10 -10
  75. nautobot/ipam/api/urls.py +2 -1
  76. nautobot/ipam/api/views.py +11 -0
  77. nautobot/ipam/tables.py +22 -2
  78. nautobot/ipam/tests/test_graphql.py +3 -2
  79. nautobot/ipam/tests/test_views.py +0 -1
  80. nautobot/ipam/views.py +9 -9
  81. nautobot/project-static/css/base.css +0 -1
  82. nautobot/project-static/docs/404.html +3 -24
  83. nautobot/project-static/docs/apps/index.html +3 -24
  84. nautobot/project-static/docs/apps/nautobot-apps.html +3 -24
  85. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +3 -24
  86. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +3 -24
  87. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +5 -26
  88. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +3 -24
  89. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +3 -24
  90. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +3 -24
  91. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +3 -24
  92. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +3 -24
  93. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +3 -24
  94. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +3 -24
  95. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +3 -24
  96. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +3 -24
  97. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +3 -24
  98. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +3 -24
  99. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +3 -24
  100. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +3 -24
  101. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +3 -24
  102. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +3 -24
  103. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +3 -24
  104. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +3 -24
  105. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +3 -24
  106. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +3 -242
  107. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +5 -69
  108. nautobot/project-static/docs/development/apps/api/configuration-view.html +3 -24
  109. nautobot/project-static/docs/development/apps/api/database-backend-config.html +3 -24
  110. nautobot/project-static/docs/development/apps/api/models/django-admin.html +3 -24
  111. nautobot/project-static/docs/development/apps/api/models/global-search.html +3 -24
  112. nautobot/project-static/docs/development/apps/api/models/graphql.html +3 -24
  113. nautobot/project-static/docs/development/apps/api/models/index.html +3 -24
  114. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +3 -24
  115. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +3 -24
  116. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +3 -24
  117. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +3 -24
  118. nautobot/project-static/docs/development/apps/api/platform-features/index.html +3 -24
  119. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +3 -24
  120. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +3 -24
  121. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +3 -24
  122. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +3 -24
  123. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +3 -24
  124. nautobot/project-static/docs/development/apps/api/prometheus.html +3 -24
  125. nautobot/project-static/docs/development/apps/api/setup.html +3 -24
  126. nautobot/project-static/docs/development/apps/api/testing.html +3 -24
  127. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +3 -24
  128. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +3 -24
  129. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +3 -24
  130. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +3 -24
  131. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +3 -24
  132. nautobot/project-static/docs/development/apps/api/views/base-template.html +3 -24
  133. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +12 -38
  134. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +15 -41
  135. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +3 -24
  136. nautobot/project-static/docs/development/apps/api/views/index.html +3 -24
  137. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +3 -24
  138. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +3 -24
  139. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +3 -24
  140. nautobot/project-static/docs/development/apps/api/views/notes.html +3 -24
  141. nautobot/project-static/docs/development/apps/api/views/rest-api.html +3 -24
  142. nautobot/project-static/docs/development/apps/api/views/urls.html +3 -24
  143. nautobot/project-static/docs/development/apps/index.html +3 -24
  144. nautobot/project-static/docs/development/apps/migration/code-updates.html +3 -24
  145. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +3 -24
  146. nautobot/project-static/docs/development/apps/migration/from-v1.html +3 -24
  147. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +3 -24
  148. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +3 -24
  149. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +3 -24
  150. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +3 -24
  151. nautobot/project-static/docs/development/apps/porting-from-netbox.html +3 -24
  152. nautobot/project-static/docs/development/core/application-registry.html +3 -24
  153. nautobot/project-static/docs/development/core/best-practices.html +3 -24
  154. nautobot/project-static/docs/development/core/bootstrap-ui.html +3 -24
  155. nautobot/project-static/docs/development/core/caching.html +3 -24
  156. nautobot/project-static/docs/development/core/controllers.html +204 -35
  157. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +3 -24
  158. nautobot/project-static/docs/development/core/extending-models.html +3 -24
  159. nautobot/project-static/docs/development/core/generic-views.html +3 -24
  160. nautobot/project-static/docs/development/core/getting-started.html +13 -43
  161. nautobot/project-static/docs/development/core/homepage.html +3 -24
  162. nautobot/project-static/docs/development/core/index.html +3 -24
  163. nautobot/project-static/docs/development/core/model-features.html +3 -24
  164. nautobot/project-static/docs/development/core/natural-keys.html +3 -24
  165. nautobot/project-static/docs/development/core/navigation-menu.html +3 -24
  166. nautobot/project-static/docs/development/core/release-checklist.html +3 -24
  167. nautobot/project-static/docs/development/core/role-internals.html +3 -24
  168. nautobot/project-static/docs/development/core/settings.html +3 -24
  169. nautobot/project-static/docs/development/core/style-guide.html +3 -24
  170. nautobot/project-static/docs/development/core/templates.html +3 -24
  171. nautobot/project-static/docs/development/core/testing.html +3 -24
  172. nautobot/project-static/docs/development/core/user-preferences.html +3 -24
  173. nautobot/project-static/docs/development/index.html +3 -24
  174. nautobot/project-static/docs/development/jobs/index.html +3 -24
  175. nautobot/project-static/docs/development/jobs/migration/from-v1.html +3 -24
  176. nautobot/project-static/docs/index.html +3 -24
  177. nautobot/project-static/docs/models/dcim/{controllermanageddevicegroup.html → controllerdevicegroup.html} +3 -3
  178. nautobot/project-static/docs/objects.inv +0 -0
  179. nautobot/project-static/docs/release-notes/index.html +3 -24
  180. nautobot/project-static/docs/release-notes/version-1.0.html +3 -24
  181. nautobot/project-static/docs/release-notes/version-1.1.html +3 -24
  182. nautobot/project-static/docs/release-notes/version-1.2.html +3 -24
  183. nautobot/project-static/docs/release-notes/version-1.3.html +3 -24
  184. nautobot/project-static/docs/release-notes/version-1.4.html +3 -24
  185. nautobot/project-static/docs/release-notes/version-1.5.html +3 -24
  186. nautobot/project-static/docs/release-notes/version-1.6.html +3 -24
  187. nautobot/project-static/docs/release-notes/version-2.0.html +3 -24
  188. nautobot/project-static/docs/release-notes/version-2.1.html +162 -411
  189. nautobot/project-static/docs/release-notes/version-2.2.html +30 -212
  190. nautobot/project-static/docs/search/search_index.json +1 -1
  191. nautobot/project-static/docs/sitemap.xml +255 -260
  192. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  193. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +3 -24
  194. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +3 -24
  195. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +3 -24
  196. nautobot/project-static/docs/user-guide/administration/configuration/index.html +3 -24
  197. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +3 -24
  198. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +3 -24
  199. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +3 -24
  200. nautobot/project-static/docs/user-guide/administration/guides/caching.html +3 -24
  201. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +3 -24
  202. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +3 -24
  203. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +3 -24
  204. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +3 -24
  205. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +3 -24
  206. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +3 -24
  207. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +3 -24
  208. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +3 -24
  209. nautobot/project-static/docs/user-guide/administration/installation/docker.html +6 -31
  210. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +3 -24
  211. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -24
  212. nautobot/project-static/docs/user-guide/administration/installation/index.html +3 -24
  213. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +3 -24
  214. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +3 -24
  215. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +6 -27
  216. nautobot/project-static/docs/user-guide/administration/installation/services.html +3 -24
  217. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +3 -24
  218. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +3 -24
  219. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +3 -24
  220. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +3 -24
  221. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +3 -24
  222. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +3 -24
  223. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +3 -24
  224. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +3 -24
  225. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +3 -24
  226. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +3 -24
  227. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +3 -24
  228. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +3 -24
  229. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +3 -24
  230. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +3 -24
  231. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +3 -24
  232. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +3 -24
  233. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +3 -24
  234. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +3 -24
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +3 -24
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +3 -24
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +3 -24
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +3 -24
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +3 -24
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +11 -259
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/{controllermanageddevicegroup.html → controllerdevicegroup.html} +17 -107
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +3 -24
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +3 -24
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +3 -24
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +3 -24
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +3 -24
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +3 -24
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +3 -24
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +3 -24
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +3 -24
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +3 -24
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +3 -24
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +3 -24
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +3 -24
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +3 -24
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +3 -24
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +3 -24
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +3 -24
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +3 -24
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +3 -24
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +3 -24
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +3 -24
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +3 -24
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +3 -24
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +3 -24
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +3 -24
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +3 -24
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +3 -24
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +3 -24
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +3 -24
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +3 -24
  272. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +6 -27
  273. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +3 -24
  274. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +3 -24
  275. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +3 -24
  276. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +3 -24
  277. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +3 -24
  278. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +3 -24
  279. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +3 -24
  280. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +3 -24
  281. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +3 -24
  282. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +3 -24
  283. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +3 -24
  284. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +3 -24
  285. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +3 -24
  286. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +3 -24
  287. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +3 -24
  288. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +3 -24
  289. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +3 -24
  290. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +3 -24
  291. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +3 -24
  292. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +3 -24
  293. nautobot/project-static/docs/user-guide/feature-guides/{contacts-and-teams.html → contact-and-team.html} +4 -25
  294. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +5 -26
  295. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +3 -24
  296. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +3 -24
  297. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +3 -24
  298. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +3 -24
  299. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +3 -24
  300. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +3 -24
  301. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +5 -26
  302. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +3 -24
  303. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +3 -24
  304. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +3 -24
  305. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +3 -24
  306. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +3 -24
  307. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +3 -24
  308. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +3 -24
  309. nautobot/project-static/docs/user-guide/index.html +3 -24
  310. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +3 -24
  311. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +3 -24
  312. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +3 -24
  313. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +3 -24
  314. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +3 -24
  315. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +3 -24
  316. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +3 -24
  317. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +3 -24
  318. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +3 -24
  319. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +3 -24
  320. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +3 -24
  321. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +3 -24
  322. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +3 -24
  323. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +3 -24
  324. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +3 -24
  325. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +3 -24
  326. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +3 -24
  327. nautobot/project-static/docs/user-guide/platform-functionality/note.html +3 -24
  328. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +3 -24
  329. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +3 -24
  330. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +3 -24
  331. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +3 -24
  332. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +3 -24
  333. nautobot/project-static/docs/user-guide/platform-functionality/role.html +3 -24
  334. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +3 -24
  335. nautobot/project-static/docs/user-guide/platform-functionality/status.html +3 -24
  336. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +3 -24
  337. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +3 -24
  338. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +3 -24
  339. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +3 -24
  340. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +3 -24
  341. nautobot/tenancy/api/urls.py +2 -1
  342. nautobot/tenancy/api/views.py +12 -0
  343. nautobot/tenancy/tables.py +1 -1
  344. nautobot/tenancy/tests/test_views.py +0 -1
  345. nautobot/users/api/urls.py +2 -1
  346. nautobot/users/api/views.py +65 -2
  347. nautobot/users/views.py +8 -8
  348. nautobot/virtualization/api/urls.py +2 -1
  349. nautobot/virtualization/api/views.py +12 -0
  350. {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/METADATA +3 -3
  351. {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/RECORD +356 -361
  352. nautobot/core/tests/integration/test_view_authentication.py +0 -67
  353. nautobot/dcim/management/commands/migrate_location_contacts.py +0 -218
  354. nautobot/dcim/templates/dcim/controller_create.html +0 -70
  355. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +0 -88
  356. nautobot/ipam/tests/test_tables.py +0 -42
  357. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +0 -8581
  358. /nautobot/dcim/templates/dcim/{controllermanageddevicegroup_retrieve.html → controllerdevicegroup_retrieve.html} +0 -0
  359. {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/LICENSE.txt +0 -0
  360. {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/NOTICE +0 -0
  361. {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/WHEEL +0 -0
  362. {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/entry_points.txt +0 -0
@@ -85,7 +85,7 @@ def hyperlinked_object(value, field="display"):
85
85
  @register.filter()
86
86
  def hyperlinked_email(value):
87
87
  """Render an email address as a `mailto:` hyperlink."""
88
- if not value:
88
+ if value is None:
89
89
  return placeholder(value)
90
90
  return format_html('<a href="mailto:{}">{}</a>', value, value)
91
91
 
@@ -94,7 +94,7 @@ def hyperlinked_email(value):
94
94
  @register.filter()
95
95
  def hyperlinked_phone_number(value):
96
96
  """Render a phone number as a `tel:` hyperlink."""
97
- if not value:
97
+ if value is None:
98
98
  return placeholder(value)
99
99
  return format_html('<a href="tel:{}">{}</a>', value, value)
100
100
 
@@ -17,7 +17,6 @@ from tree_queries.models import TreeNode
17
17
 
18
18
  from nautobot.core import testing
19
19
  from nautobot.core.models.generics import PrimaryModel
20
- from nautobot.core.models.tree_queries import TreeModel
21
20
  from nautobot.core.templatetags import helpers
22
21
  from nautobot.core.testing import mixins
23
22
  from nautobot.core.utils import lookup
@@ -25,7 +24,6 @@ from nautobot.extras import choices as extras_choices, models as extras_models,
25
24
  from nautobot.extras.forms import CustomFieldModelFormMixin, RelationshipModelFormMixin
26
25
  from nautobot.extras.models import CustomFieldModel, RelationshipModel
27
26
  from nautobot.extras.models.mixins import NotesMixin
28
- from nautobot.ipam.models import Prefix
29
27
  from nautobot.users import models as users_models
30
28
 
31
29
  __all__ = (
@@ -724,8 +722,6 @@ class ViewTestCases:
724
722
  """
725
723
 
726
724
  filterset = None
727
- filter_on_field = "name"
728
- sort_on_field = "tags"
729
725
 
730
726
  def get_filterset(self):
731
727
  return self.filterset or lookup.get_filterset_for_model(self.model)
@@ -741,32 +737,6 @@ class ViewTestCases:
741
737
  def get_list_view(self):
742
738
  return lookup.get_view_for_model(self.model, view_type="List")
743
739
 
744
- def test_table_with_indentation_is_removed_on_filter_or_sort(self):
745
- self.user.is_superuser = True
746
- self.user.save()
747
-
748
- if not issubclass(self.model, (TreeModel)) and self.model is not Prefix:
749
- self.skipTest("Skipping Non TreeModels")
750
-
751
- with self.subTest("Assert indentation is present"):
752
- response = self.client.get(f"{self._get_url('list')}")
753
- response_body = response.content.decode(response.charset)
754
- self.assertInHTML('<i class="mdi mdi-circle-small"></i>', response_body)
755
-
756
- with self.subTest("Assert indentation is removed on filter"):
757
- queryset = (
758
- self._get_queryset().filter(parent__isnull=False).values_list(self.filter_on_field, flat=True)[:5]
759
- )
760
- filter_values = "&".join([f"{self.filter_on_field}={instance_value}" for instance_value in queryset])
761
- response = self.client.get(f"{self._get_url('list')}?{filter_values}")
762
- response_body = response.content.decode(response.charset)
763
- self.assertNotIn('<i class="mdi mdi-circle-small"></i>', response_body)
764
-
765
- with self.subTest("Assert indentation is removed on sort"):
766
- response = self.client.get(f"{self._get_url('list')}?sort={self.sort_on_field}")
767
- response_body = response.content.decode(response.charset)
768
- self.assertNotIn('<i class="mdi mdi-circle-small"></i>', response_body)
769
-
770
740
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
771
741
  def test_list_objects_anonymous(self):
772
742
  # Make the request as an unauthenticated user
@@ -770,7 +770,6 @@ class APIOrderingTestCase(testing.APITestCase):
770
770
  "TextField": "admin_contact",
771
771
  "DateTimeField": "created",
772
772
  }
773
- cls.maxDiff = None
774
773
 
775
774
  def _validate_sorted_response(self, response, queryset, field_name, is_fk_field=False):
776
775
  self.assertHttpStatus(response, 200)
@@ -795,24 +794,18 @@ class APIOrderingTestCase(testing.APITestCase):
795
794
  """Tests that results are returned in the expected ascending order."""
796
795
 
797
796
  for field_type, field_name in self.field_type_map.items():
798
- with self.subTest(f"Testing {field_type} {field_name}"):
799
- # Use `name` as a secondary sort as fields like `asn` and `admin_contact` may be null
800
- response = self.client.get(f"{self.url}?sort={field_name},name&limit=10", **self.header)
801
- self._validate_sorted_response(
802
- response, Provider.objects.all().order_by(field_name, "name"), field_name
803
- )
797
+ with self.subTest(f"Testing {field_type}"):
798
+ response = self.client.get(f"{self.url}?sort={field_name}&limit=10", **self.header)
799
+ self._validate_sorted_response(response, Provider.objects.all().order_by(field_name), field_name)
804
800
 
805
801
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
806
802
  def test_descending_sort(self):
807
803
  """Tests that results are returned in the expected descending order."""
808
804
 
809
805
  for field_type, field_name in self.field_type_map.items():
810
- with self.subTest(f"Testing {field_type} {field_name}"):
811
- # Use `name` as a secondary sort as fields like `asn` and `admin_contact` may be null
812
- response = self.client.get(f"{self.url}?sort=-{field_name},name&limit=10", **self.header)
813
- self._validate_sorted_response(
814
- response, Provider.objects.all().order_by(f"-{field_name}", "name"), field_name
815
- )
806
+ with self.subTest(f"Testing {field_type}"):
807
+ response = self.client.get(f"{self.url}?sort=-{field_name}&limit=10", **self.header)
808
+ self._validate_sorted_response(response, Provider.objects.all().order_by(f"-{field_name}"), field_name)
816
809
 
817
810
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
818
811
  def test_sorting_tree_node_models(self):
@@ -4,7 +4,7 @@ from django.urls import reverse
4
4
 
5
5
  from nautobot.core.constants import CSV_NO_OBJECT, CSV_NULL_TYPE, VARBINARY_IP_FIELD_REPR_OF_CSV_NO_OBJECT
6
6
  from nautobot.dcim.api.serializers import DeviceSerializer
7
- from nautobot.dcim.models.devices import Controller, Device, DeviceType
7
+ from nautobot.dcim.models.devices import Device, DeviceType
8
8
  from nautobot.dcim.models.locations import Location
9
9
  from nautobot.extras.models.roles import Role
10
10
  from nautobot.extras.models.statuses import Status
@@ -25,7 +25,6 @@ class CSVParsingRelatedTestCase(TestCase):
25
25
  devicerole = Role.objects.get_for_model(Device).first()
26
26
  device_status = Status.objects.get_for_model(Device).first()
27
27
  tags = Tag.objects.get_for_model(Device).all()[:3]
28
- Controller.objects.filter(controller_device__isnull=False).delete()
29
28
  Device.objects.all().delete()
30
29
  self.device = Device.objects.create(
31
30
  device_type=devicetype,
@@ -93,7 +92,7 @@ class CSVParsingRelatedTestCase(TestCase):
93
92
  "primary_ip6__host",
94
93
  "cluster__name",
95
94
  "virtual_chassis__name",
96
- "controller_managed_device_group__name",
95
+ "controller_device_group__name",
97
96
  "device_redundancy_group__name",
98
97
  "software_version__platform__name",
99
98
  "software_version__version",
@@ -120,7 +119,7 @@ class CSVParsingRelatedTestCase(TestCase):
120
119
  "primary_ip6",
121
120
  "cluster",
122
121
  "virtual_chassis",
123
- "controller_managed_device_group",
122
+ "controller_device_group",
124
123
  "device_redundancy_group",
125
124
  "secrets_group",
126
125
  ]
@@ -225,7 +224,7 @@ class CSVParsingRelatedTestCase(TestCase):
225
224
  "primary_ip6__host": CSV_NO_OBJECT,
226
225
  "cluster__name": CSV_NO_OBJECT,
227
226
  "virtual_chassis__name": CSV_NO_OBJECT,
228
- "controller_managed_device_group__name": CSV_NO_OBJECT,
227
+ "controller_device_group__name": CSV_NO_OBJECT,
229
228
  "device_redundancy_group__name": CSV_NO_OBJECT,
230
229
  "software_version__platform__name": CSV_NO_OBJECT,
231
230
  "software_version__version": CSV_NO_OBJECT,
@@ -17,7 +17,7 @@ from nautobot.core.constants import CHARFIELD_MAX_LENGTH
17
17
  from nautobot.core.models import fields as core_fields
18
18
  from nautobot.core.utils import lookup
19
19
  from nautobot.dcim import choices as dcim_choices, filters as dcim_filters, models as dcim_models
20
- from nautobot.dcim.models import Controller, Device
20
+ from nautobot.dcim.models import Device
21
21
  from nautobot.extras import models as extras_models
22
22
  from nautobot.extras.utils import FeatureQuery
23
23
  from nautobot.ipam import models as ipam_models
@@ -830,7 +830,6 @@ class DynamicFilterLookupExpressionTest(TestCase):
830
830
  @classmethod
831
831
  def setUpTestData(cls):
832
832
  manufacturers = dcim_models.Manufacturer.objects.all()[:3]
833
- Controller.objects.filter(controller_device__isnull=False).delete()
834
833
  Device.objects.all().delete()
835
834
 
836
835
  device_types = (
@@ -43,7 +43,6 @@ from nautobot.dcim.models import (
43
43
  Cable,
44
44
  ConsolePort,
45
45
  ConsoleServerPort,
46
- Controller,
47
46
  Device,
48
47
  DeviceType,
49
48
  FrontPort,
@@ -631,9 +630,21 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
631
630
  self.assertEqual(names, ["Rack 1-1", "Rack 1-2", "Rack 2-1", "Rack 2-2"])
632
631
 
633
632
  def test_graphql_api_no_token(self):
634
- """Validate unauthenticated users are not able to query anything."""
633
+ """Validate unauthenticated users are not able to query anything by default."""
635
634
  response = self.client.post(self.api_url, {"query": self.get_racks_query}, format="json")
636
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
635
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
636
+ self.assertIsInstance(response.data["data"]["racks"], list)
637
+ names = [item["name"] for item in response.data["data"]["racks"]]
638
+ self.assertEqual(names, [])
639
+
640
+ @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
641
+ def test_graphql_api_no_token_exempt(self):
642
+ """Validate unauthenticated users are able to query based on the exempt permissions."""
643
+ response = self.client.post(self.api_url, {"query": self.get_racks_query}, format="json")
644
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
645
+ self.assertIsInstance(response.data["data"]["racks"], list)
646
+ names = [item["name"] for item in response.data["data"]["racks"]]
647
+ self.assertEqual(names, ["Rack 1-1", "Rack 1-2", "Rack 2-1", "Rack 2-2"])
637
648
 
638
649
  def test_graphql_api_wrong_token(self):
639
650
  """Validate a wrong token return 403."""
@@ -710,7 +721,6 @@ class GraphQLQueryTest(GraphQLTestCaseBase):
710
721
 
711
722
  # Remove random IPAddress and Device fixtures for this custom test
712
723
  IPAddress.objects.all().delete()
713
- Controller.objects.filter(controller_device__isnull=False).delete()
714
724
  Device.objects.all().delete()
715
725
 
716
726
  # Initialize fake request that will be required to execute GraphQL query
@@ -44,8 +44,6 @@ class NavMenuTestCase(TestCase):
44
44
  expected_name = "Interfaces"
45
45
  elif expected_name == "Object Changes":
46
46
  expected_name = "Change Log"
47
- elif expected_name == "Controller Managed Device Groups":
48
- expected_name = "Managed Device Groups"
49
47
  self.assertEqual(item_details["name"], expected_name)
50
48
  if item_url == get_route_for_model(view_model, "list"):
51
49
  # Not assertEqual as some menu items have additional permissions defined.
@@ -89,7 +87,6 @@ class NavMenuTestCase(TestCase):
89
87
  self.assertEqual(expected_perms[tab_name], tab_details["permissions"])
90
88
 
91
89
 
92
- @tag("unit")
93
90
  class NewUINavTest(TestCase):
94
91
  @patch.dict(registry, values={"new_ui_nav_menu": {}}, clear=True)
95
92
  def test_build_new_ui_nav_menu(self):
@@ -305,31 +305,25 @@ class LoginUITestCase(TestCase):
305
305
  sso_login_search_result = self.make_request()
306
306
  self.assertIsNotNone(sso_login_search_result)
307
307
 
308
- def test_graphql_redirects_back_to_login_unauthenticated(self):
309
- """Assert that graphql redirects to login page if user is unauthenticated."""
308
+ @override_settings(BANNER_TOP="Hello, Banner Top", BANNER_BOTTOM="Hello, Banner Bottom")
309
+ def test_routes_redirect_back_to_login_unauthenticated(self):
310
+ """Assert that api docs and graphql redirects to login page if user is unauthenticated."""
310
311
  self.client.logout()
311
312
  headers = {"HTTP_ACCEPT": "text/html"}
312
- url = reverse("graphql")
313
- response = self.client.get(url, follow=True, **headers)
314
- self.assertHttpStatus(response, 200)
315
- self.assertRedirects(response, f"/login/?next={url}")
316
- response_content = response.content.decode(response.charset).replace("\n", "")
317
- for footer_text in self.footer_elements:
318
- self.assertNotIn(footer_text, response_content)
319
-
320
- def test_api_docs_403_unauthenticated(self):
321
- """Assert that api docs return a 403 Forbidden if user is unauthenticated."""
322
- self.client.logout()
323
- urls = [
324
- reverse("api_docs"),
325
- reverse("api_redocs"),
326
- reverse("schema"),
327
- reverse("schema_json"),
328
- reverse("schema_yaml"),
329
- ]
313
+ urls = [reverse("api_docs"), reverse("graphql")]
330
314
  for url in urls:
331
- response = self.client.get(url)
332
- self.assertHttpStatus(response, 403)
315
+ response = self.client.get(url, follow=True, **headers)
316
+ self.assertHttpStatus(response, 200)
317
+ redirect_chain = [(f"/login/?next={url}", 302)]
318
+ self.assertEqual(response.redirect_chain, redirect_chain)
319
+ response_content = response.content.decode(response.charset).replace("\n", "")
320
+ # Assert Footer items(`self.footer_elements`), Banner and Banner Top is hidden
321
+ for footer_text in self.footer_elements:
322
+ self.assertNotIn(footer_text, response_content)
323
+ # Only API Docs implements BANNERS
324
+ if url == urls[0]:
325
+ self.assertNotIn("Hello, Banner Top", response_content)
326
+ self.assertNotIn("Hello, Banner Bottom", response_content)
333
327
 
334
328
 
335
329
  class MetricsViewTestCase(TestCase):
@@ -1,14 +1,12 @@
1
1
  """Utilities for looking up related classes and information."""
2
2
 
3
3
  import inspect
4
- import re
5
4
 
6
5
  from django.apps import apps
7
6
  from django.conf import settings
8
7
  from django.contrib.auth.models import Group
9
8
  from django.contrib.contenttypes.models import ContentType
10
9
  from django.db.models import Model
11
- from django.urls import get_resolver, URLPattern, URLResolver
12
10
  from django.utils.module_loading import import_string
13
11
 
14
12
 
@@ -234,125 +232,3 @@ def get_created_and_last_updated_usernames_for_model(instance):
234
232
  last_updated_by = last_updated_by_record.user_name
235
233
 
236
234
  return created_by, last_updated_by
237
-
238
-
239
- def get_url_patterns(urlconf=None, patterns_list=None, base_path="/"):
240
- """
241
- Recursively yield a list of registered URL patterns.
242
-
243
- Args:
244
- urlconf (URLConf): Python module such as `nautobot.core.urls`.
245
- Default if unspecified is the value of `settings.ROOT_URLCONF`, i.e. the `nautobot.core.urls` module.
246
- patterns_list (list): Used in recursion. Generally can be omitted on initial call.
247
- Default if unspecified is the `url_patterns` attribute of the given `urlconf` module.
248
- base_path (str): String to prepend to all URL patterns yielded.
249
- Default if unspecified is the string `"/"`.
250
-
251
- Yields:
252
- (str): Each URL pattern defined in the given urlconf and its descendants
253
-
254
- Examples:
255
- >>> generator = get_url_patterns()
256
- >>> next(generator)
257
- '/'
258
- >>> next(generator)
259
- '/search/'
260
- >>> next(generator)
261
- '/login/'
262
- >>> next(generator)
263
- '/logout/'
264
- >>> next(generator)
265
- '/circuits/circuits/<uuid:pk>/terminations/swap/'
266
-
267
- >>> import example_plugin.urls as example_urls
268
- >>> for url_pattern in get_url_patterns(example_urls, base_path="/plugins/example-app/"):
269
- ... print(url_pattern)
270
- ...
271
- /plugins/example-app/
272
- /plugins/example-app/config/
273
- /plugins/example-app/models/<uuid:pk>/dynamic-groups/
274
- /plugins/example-app/other-models/<uuid:pk>/dynamic-groups/
275
- /plugins/example-app/docs/
276
- /plugins/example-app/circuits/<uuid:pk>/example-app-tab/
277
- /plugins/example-app/devices/<uuid:pk>/example-app-tab-1/
278
- /plugins/example-app/devices/<uuid:pk>/example-app-tab-2/
279
- /plugins/example-app/override-target/
280
- /plugins/example-app/^models/$
281
- /plugins/example-app/^models/add/$
282
- /plugins/example-app/^models/import/$
283
- /plugins/example-app/^models/edit/$
284
- /plugins/example-app/^models/delete/$
285
- /plugins/example-app/^models/all-names/$
286
- /plugins/example-app/^models/(?P<pk>[^/.]+)/$
287
- /plugins/example-app/^models/(?P<pk>[^/.]+)/delete/$
288
- /plugins/example-app/^models/(?P<pk>[^/.]+)/edit/$
289
- /plugins/example-app/^models/(?P<pk>[^/.]+)/changelog/$
290
- /plugins/example-app/^models/(?P<pk>[^/.]+)/notes/$
291
- /plugins/example-app/^other-models/$
292
- /plugins/example-app/^other-models/add/$
293
- /plugins/example-app/^other-models/edit/$
294
- /plugins/example-app/^other-models/delete/$
295
- /plugins/example-app/^other-models/(?P<pk>[^/.]+)/$
296
- /plugins/example-app/^other-models/(?P<pk>[^/.]+)/delete/$
297
- /plugins/example-app/^other-models/(?P<pk>[^/.]+)/edit/$
298
- /plugins/example-app/^other-models/(?P<pk>[^/.]+)/changelog/$
299
- /plugins/example-app/^other-models/(?P<pk>[^/.]+)/notes/$
300
- """
301
- if urlconf is None:
302
- urlconf = settings.ROOT_URLCONF
303
- if patterns_list is None:
304
- patterns_list = get_resolver(urlconf).url_patterns
305
-
306
- for item in patterns_list:
307
- if isinstance(item, URLPattern):
308
- yield base_path + str(item.pattern)
309
- elif isinstance(item, URLResolver):
310
- # Recurse!
311
- yield from get_url_patterns(urlconf, item.url_patterns, base_path + str(item.pattern))
312
-
313
-
314
- def get_url_for_url_pattern(url_pattern):
315
- """
316
- Given a URL pattern, construct a URL string that would match that pattern.
317
-
318
- Examples:
319
- >>> get_url_for_url_pattern("/plugins/example-app/^models/(?P<pk>[^/.]+)/$")
320
- '/plugins/example-app/models/00000000-0000-0000-0000-000000000000/'
321
- >>> get_url_for_url_pattern("/circuits/circuit-terminations/<uuid:termination_a_id>/connect/<str:termination_b_type>/")
322
- '/circuits/circuit-terminations/00000000-0000-0000-0000-000000000000/connect/string/'
323
- """
324
- url = url_pattern
325
- # Fixup tokens in path-style "classic" view URLs:
326
- # "/admin/users/user/<id>/password/"
327
- url = re.sub(r"<id>", "00000000-0000-0000-0000-000000000000", url)
328
- # "/silk/request/<uuid:request_id>/profile/<int:profile_id>/"
329
- url = re.sub(r"<int:\w+>", "1", url)
330
- # "/admin/admin/logentry/<path:object_id>/"
331
- url = re.sub(r"<path:\w+>", "1", url)
332
- # "/dcim/sites/<slug:slug>/"
333
- url = re.sub(r"<slug:\w+>", "slug", url)
334
- # "/apps/installed-apps/<str:app>/"
335
- url = re.sub(r"<str:\w+>", "string", url)
336
- # "/dcim/locations/<uuid:pk>/"
337
- url = re.sub(r"<uuid:\w+>", "00000000-0000-0000-0000-000000000000", url)
338
- # tokens in regexp-style router urls, including REST and NautobotUIViewSet:
339
- # "/extras/^external-integrations/(?P<pk>[^/.]+)/$"
340
- # "/api/virtualization/^interfaces/(?P<pk>[^/.]+)/$"
341
- # "/api/virtualization/^interfaces/(?P<pk>[^/.]+)\\.(?P<format>[a-z0-9]+)/?$"
342
- url = re.sub(r"[$^]", "", url)
343
- url = re.sub(r"/\?", "/", url)
344
- url = re.sub(r"\(\?P<app_label>[^)]+\)", "users", url)
345
- url = re.sub(r"\(\?P<class_path>[^)]+\)", "foo/bar/baz", url)
346
- url = re.sub(r"\(\?P<format>[^)]+\)", "json", url)
347
- url = re.sub(r"\(\?P<name>[^)]+\)", "string", url)
348
- url = re.sub(r"\(\?P<pk>[^)]+\)", "00000000-0000-0000-0000-000000000000", url)
349
- url = re.sub(r"\(\?P<slug>[^)]+\)", "string", url)
350
- url = re.sub(r"\(\?P<url>[^)]+\)", "any", url)
351
- # Fallthru for generic URL parameters
352
- url = re.sub(r"\(\?P<\w+>[^)]+\)\??", "unknown", url)
353
- url = re.sub(r"\\", "", url)
354
-
355
- if any(char in url for char in "<>[]()?+^$"):
356
- raise RuntimeError(f"Unhandled token in URL {url} derived from {url_pattern}")
357
-
358
- return url
@@ -11,7 +11,7 @@ from django.contrib.auth.decorators import permission_required
11
11
  from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
12
12
  from django.contrib.contenttypes.models import ContentType
13
13
  from django.http import HttpResponseForbidden, HttpResponseServerError, JsonResponse
14
- from django.shortcuts import get_object_or_404, render
14
+ from django.shortcuts import get_object_or_404, redirect, render
15
15
  from django.template import loader, RequestContext, Template
16
16
  from django.template.exceptions import TemplateDoesNotExist
17
17
  from django.urls import resolve, reverse
@@ -210,7 +210,7 @@ class SearchView(AccessMixin, View):
210
210
  )
211
211
 
212
212
 
213
- class StaticMediaFailureView(View): # NOT using LoginRequiredMixin here as this may happen even on the login page
213
+ class StaticMediaFailureView(View):
214
214
  """
215
215
  Display a user-friendly error message with troubleshooting tips when a static media file fails to load.
216
216
  """
@@ -265,8 +265,12 @@ def csrf_failure(request, reason="", template_name="403_csrf_failure.html"):
265
265
  return HttpResponseForbidden(t.render(context), content_type="text/html")
266
266
 
267
267
 
268
- class CustomGraphQLView(LoginRequiredMixin, GraphQLView):
268
+ class CustomGraphQLView(GraphQLView):
269
269
  def render_graphiql(self, request, **data):
270
+ if not request.user.is_authenticated:
271
+ graphql_url = reverse("graphql")
272
+ login_url = reverse(settings.LOGIN_URL)
273
+ return redirect(f"{login_url}?next={graphql_url}")
270
274
  query_name = request.GET.get("name")
271
275
  if query_name:
272
276
  data["obj"] = GraphQLQuery.objects.get(name=query_name)
@@ -4,7 +4,6 @@ import re
4
4
 
5
5
  from django.conf import settings
6
6
  from django.contrib import messages
7
- from django.contrib.auth.mixins import LoginRequiredMixin
8
7
  from django.contrib.contenttypes.models import ContentType
9
8
  from django.core.exceptions import (
10
9
  FieldDoesNotExist,
@@ -59,14 +58,6 @@ from nautobot.extras.tables import AssociatedContactsTable
59
58
  from nautobot.extras.utils import remove_prefix_from_cf_key
60
59
 
61
60
 
62
- class GenericView(LoginRequiredMixin, View):
63
- """
64
- Base class for non-object-related views.
65
-
66
- Enforces authentication, which Django's base View does not by default.
67
- """
68
-
69
-
70
61
  class ObjectView(ObjectPermissionRequiredMixin, View):
71
62
  """
72
63
  Retrieve a single object for display.
@@ -229,7 +220,6 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
229
220
  display_filter_params = []
230
221
  dynamic_filter_form = None
231
222
  filter_form = None
232
- hide_hierarchy_ui = False
233
223
 
234
224
  if self.filterset:
235
225
  filter_params = self.get_filter_params(request)
@@ -242,12 +232,6 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
242
232
  )
243
233
  self.queryset = self.queryset.none()
244
234
 
245
- # If a valid filterset is applied, we have to hide the hierarchy indentation in the UI for tables that support hierarchy indentation.
246
- # NOTE: An empty filterset query-param is also valid filterset and we dont want to hide hierarchy indentation if no filter query-param is provided
247
- # hence `filterset.data`.
248
- if filterset.is_valid() and filterset.data:
249
- hide_hierarchy_ui = True
250
-
251
235
  display_filter_params = [
252
236
  check_filter_for_display(filterset.filters, field_name, values)
253
237
  for field_name, values in filter_params.items()
@@ -299,9 +283,9 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
299
283
  table_config_form = None
300
284
  if self.table:
301
285
  # Construct the objects table
302
- if self.request.GET.getlist("sort"):
303
- hide_hierarchy_ui = True # hide tree hierarchy if custom sort is used
304
- table = self.table(self.queryset, user=request.user, hide_hierarchy_ui=hide_hierarchy_ui)
286
+ # Order By is needed in the table `__init__` method
287
+ order_by = self.request.GET.getlist("sort")
288
+ table = self.table(self.queryset, user=request.user, order_by=order_by)
305
289
  if "pk" in table.base_columns and (permissions["change"] or permissions["delete"]):
306
290
  table.columns.show("pk")
307
291
 
@@ -608,7 +608,6 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
608
608
  action_buttons = ("add", "import", "export")
609
609
  filterset_class = None
610
610
  filterset_form_class = None
611
- hide_hierarchy_ui = False
612
611
  non_filter_params = (
613
612
  "export", # trigger for CSV/export-template/YAML export # 3.0 TODO: remove, irrelevant after #4746
614
613
  "page", # used by django-tables2.RequestConfig
@@ -630,12 +629,6 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
630
629
  format_html("Invalid filters were specified: {}", self.filterset.errors),
631
630
  )
632
631
  queryset = queryset.none()
633
-
634
- # If a valid filterset is applied, we have to hide the hierarchy indentation in the UI for tables that support hierarchy indentation.
635
- # NOTE: An empty filterset query-param is also valid filterset and we dont want to hide hierarchy indentation if no filter query-param is provided
636
- # hence `filterset.data`.
637
- if self.filterset.is_valid() and self.filterset.data:
638
- self.hide_hierarchy_ui = True
639
632
  return queryset
640
633
 
641
634
  # 3.0 TODO: remove, irrelevant after #4746
@@ -65,13 +65,10 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
65
65
  table_class = view.get_table_class()
66
66
  request = kwargs.get("request", view.request)
67
67
  queryset = view.alter_queryset(request)
68
-
69
68
  if view.action in ["list", "notes", "changelog"]:
70
69
  if view.action == "list":
71
70
  permissions = kwargs.get("permissions", {})
72
- if view.request.GET.getlist("sort"):
73
- view.hide_hierarchy_ui = True # hide tree hierarchy if custom sort is used
74
- table = table_class(queryset, user=request.user, hide_hierarchy_ui=view.hide_hierarchy_ui)
71
+ table = table_class(queryset, user=request.user)
75
72
  if "pk" in table.base_columns and (permissions["change"] or permissions["delete"]):
76
73
  table.columns.show("pk")
77
74
  elif view.action == "notes":
@@ -55,7 +55,7 @@ from nautobot.dcim.models import (
55
55
  ConsoleServerPort,
56
56
  ConsoleServerPortTemplate,
57
57
  Controller,
58
- ControllerManagedDeviceGroup,
58
+ ControllerDeviceGroup,
59
59
  Device,
60
60
  DeviceBay,
61
61
  DeviceBayTemplate,
@@ -598,7 +598,7 @@ class DeviceSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
598
598
  "secrets_group",
599
599
  "device_redundancy_group",
600
600
  "device_redundancy_group_priority",
601
- "controller_managed_device_group",
601
+ "controller_device_group",
602
602
  ]
603
603
  },
604
604
  },
@@ -1049,7 +1049,7 @@ class ControllerSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
1049
1049
  fields = "__all__"
1050
1050
 
1051
1051
 
1052
- class ControllerManagedDeviceGroupSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
1052
+ class ControllerDeviceGroupSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
1053
1053
  class Meta:
1054
- model = ControllerManagedDeviceGroup
1054
+ model = ControllerDeviceGroup
1055
1055
  fields = "__all__"
nautobot/dcim/api/urls.py CHANGED
@@ -2,7 +2,8 @@ from nautobot.core.api.routers import OrderedDefaultRouter
2
2
 
3
3
  from . import views
4
4
 
5
- router = OrderedDefaultRouter(view_name="DCIM")
5
+ router = OrderedDefaultRouter()
6
+ router.APIRootView = views.DCIMRootView
6
7
 
7
8
  # Locations
8
9
  router.register("location-types", views.LocationTypeViewSet)
@@ -79,7 +80,7 @@ router.register("connected-device", views.ConnectedDeviceViewSet, basename="conn
79
80
 
80
81
  # Controllers
81
82
  router.register("controllers", views.ControllerViewSet)
82
- router.register("controller-managed-device-groups", views.ControllerManagedDeviceGroupViewSet)
83
+ router.register("controller-device-groups", views.ControllerDeviceGroupViewSet)
83
84
 
84
85
  app_name = "dcim-api"
85
86
  urlpatterns = router.urls