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
@@ -1,67 +0,0 @@
1
- from django.test import tag
2
-
3
- from nautobot.core.testing import TestCase
4
- from nautobot.core.utils.lookup import get_url_for_url_pattern, get_url_patterns
5
-
6
-
7
- @tag("integration")
8
- class AuthenticationEnforcedTestCase(TestCase):
9
- r"""
10
- Test that all\* registered views require authentication to access.
11
-
12
- \* with a very small number of known exceptions such as login and logout views.
13
- """
14
-
15
- def test_all_views_require_authentication(self):
16
- self.client.logout()
17
- url_patterns = get_url_patterns()
18
-
19
- for url_pattern in url_patterns:
20
- with self.subTest(url_pattern=url_pattern):
21
- url = get_url_for_url_pattern(url_pattern)
22
- response = self.client.get(url, follow=True)
23
-
24
- if response.status_code == 405: # Method not allowed
25
- response = self.client.post(url, follow=True)
26
-
27
- # Is a view that *should* be open to unauthenticated users?
28
- if url in [
29
- "/admin/login/",
30
- "/api/plugins/example-app/webhook/",
31
- "/health/",
32
- "/login/",
33
- "/media-failure/",
34
- "/template.css",
35
- ]:
36
- self.assertHttpStatus(response, 200, msg=url)
37
- elif response.status_code == 200:
38
- # UI views generally should redirect unauthenticated users to the appropriate login page
39
- if url.startswith("/admin"):
40
- if "logout" in url:
41
- # /admin/logout/ sets next=/admin/ because having login redirect to logout would be silly
42
- redirect_url = "/admin/login/?next=/admin/"
43
- else:
44
- redirect_url = f"/admin/login/?next={url}"
45
- else:
46
- if "logout" in url:
47
- # /logout/ sets next=/ because having login redirect back to logout would be silly
48
- redirect_url = "/login/?next=/"
49
- else:
50
- redirect_url = f"/login/?next={url}"
51
- self.assertRedirects(response, redirect_url)
52
- elif response.status_code != 403:
53
- if any(
54
- url.startswith(path)
55
- for path in [
56
- "/complete/", # social auth
57
- "/login/", # social auth
58
- "/media/", # MEDIA_ROOT
59
- "/plugins/example-app/docs/", # STATIC_ROOT
60
- ]
61
- ):
62
- self.assertEqual(response.status_code, 404)
63
- else:
64
- self.fail(
65
- f"Unexpected {response.status_code} response at {url}: "
66
- + response.content.decode(response.charset)
67
- )
@@ -1,218 +0,0 @@
1
- from textwrap import dedent, indent
2
-
3
- from django.core.exceptions import ValidationError
4
- from django.core.management.base import BaseCommand
5
- from django.db import transaction
6
-
7
- from nautobot.dcim.models import Location
8
- from nautobot.extras.filters import ContactFilterSet, TeamFilterSet
9
- from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Team
10
-
11
-
12
- class Command(BaseCommand):
13
- help = "Migrate Location contact fields to Contact and Team objects."
14
- verbose_help = """
15
- This command will present a series of prompts to guide you through migrating Locations that
16
- have data in the `contact_name`, `contact_phone`, or `contact_email` fields which are not
17
- already associated to a Contact or Team. This command will give you the option to create new
18
- Contacts or Teams or, if a similar Contact or Team already exists, to link the Location to the
19
- existing Contact or Team. Note that when assigning a Location to an existing Contact or Team
20
- that has a blank `phone` or `email` field, the value from the Location will be copied to the
21
- Contact/Team. After a Location has been associated to a Contact or Team, the `contact_name`,
22
- `contact_phone`, and `contact_email` fields will be cleared from the Location.
23
- """
24
-
25
- def handle(self, *args, **kwargs):
26
- status_role_err_msg = "No {0} found for the ContactAssociation content type. Please ensure {0} are created before running this command."
27
- if not Status.objects.get_for_model(ContactAssociation).exists():
28
- self.stdout.write(self.style.ERROR(status_role_err_msg.format("statuses")))
29
- return
30
- if not Role.objects.get_for_model(ContactAssociation).exists():
31
- self.stdout.write(self.style.ERROR(status_role_err_msg.format("roles")))
32
- return
33
-
34
- self.stdout.write(self.style.NOTICE(" ".join(dedent(self.verbose_help).split("\n"))))
35
-
36
- try:
37
- with transaction.atomic():
38
- try:
39
- self.migrate_location_contacts()
40
- except KeyboardInterrupt:
41
- while True:
42
- rollback = input("\nRoll back changes? [y/n]: ").strip().lower()
43
- if rollback == "y":
44
- raise
45
- elif rollback == "n":
46
- break
47
- except KeyboardInterrupt:
48
- self.stdout.write(self.style.ERROR("\nOperation cancelled, all changes rolled back."))
49
- except:
50
- self.stdout.write(self.style.ERROR("\nOperation failed, all changes rolled back."))
51
- raise
52
-
53
- def migrate_location_contacts(self):
54
- """Iterate through Locations with contact information and try to match to existing Contact or Team."""
55
- locations_with_contact_data = (
56
- Location.objects.without_tree_fields()
57
- .exclude(
58
- contact_name="",
59
- contact_phone="",
60
- contact_email="",
61
- )
62
- .filter(associated_contacts__isnull=True)
63
- )
64
-
65
- for location in locations_with_contact_data:
66
- self.stdout.write(f"Finding existing Contacts or Teams for location {location.display}...")
67
- selected_contact = None
68
- similar_contacts = list(ContactFilterSet(data={"similar_to_location_data": [location]}).qs)
69
- similar_teams = list(TeamFilterSet(data={"similar_to_location_data": [location]}).qs)
70
- similar_contacts_and_teams = similar_contacts + similar_teams
71
-
72
- if not similar_contacts_and_teams:
73
- self.stdout.write(
74
- self.style.WARNING(f"No similar Contacts or Teams found for location {location.display}.")
75
- )
76
-
77
- else:
78
- # Found similar contacts or teams, prompt user for action
79
- self.stdout.write("")
80
- self.stdout.write(self.style.WARNING(f"Found similar contacts/teams for location {location.display}:"))
81
- self.stdout.write(f" current contact name: {location.contact_name!r}")
82
- self.stdout.write(f" current contact phone: {location.contact_phone!r}")
83
- self.stdout.write(f" current contact email: {location.contact_email!r}")
84
- self.stdout.write("")
85
-
86
- # Output menu of choices of valid contacts/teams
87
- for i, contact in enumerate(similar_contacts_and_teams, start=1):
88
- self.stdout.write(f"{self.style.WARNING(i)}: {contact._meta.model_name.title()}: {contact.name}")
89
- self.print_contact_fields(contact, rjust=len(str(i)) + len(contact._meta.model_name) + 2)
90
- self.stdout.write("")
91
-
92
- self.stdout.write(self.style.WARNING("c") + ": Create a new Contact")
93
- self.stdout.write(self.style.WARNING("t") + ": Create a new Team")
94
- self.stdout.write(self.style.WARNING("s") + ": Skip this location")
95
-
96
- # Retrieve desired contact/team from user input
97
- while True:
98
- choice = input("Select a choice from the list of items: ")
99
- if choice == "s":
100
- self.stdout.write(f"Skipping location {location.display}")
101
- break
102
- elif choice == "c":
103
- selected_contact = self.create_new_contact_from_location(location, model=Contact)
104
- break
105
- elif choice == "t":
106
- selected_contact = self.create_new_contact_from_location(location, model=Team)
107
- break
108
- elif choice.lower() == "q":
109
- raise KeyboardInterrupt
110
- elif choice.isdigit() and 0 < int(choice) <= len(similar_contacts_and_teams):
111
- selected_contact = similar_contacts_and_teams[int(choice) - 1]
112
- break
113
-
114
- if selected_contact is not None:
115
- self.associate_contact_to_location(selected_contact, location)
116
-
117
- def associate_contact_to_location(self, contact, location):
118
- role, status = self.prompt_for_role_and_status()
119
-
120
- # If email or phone fields are present in the location but not the contact, update the contact fields
121
- updated_fields = {}
122
- if location.contact_phone and not contact.phone:
123
- contact.phone = location.contact_phone
124
- updated_fields["phone"] = location.contact_phone
125
- if location.contact_email and not contact.email:
126
- contact.email = location.contact_email
127
- updated_fields["email"] = location.contact_email
128
- if updated_fields:
129
- try:
130
- contact.validated_save()
131
- self.stdout.write(
132
- self.style.SUCCESS(
133
- f"Updated {contact._meta.model_name.title()} {contact.name} field(s): {updated_fields!r}"
134
- )
135
- )
136
- except ValidationError as e:
137
- contact.refresh_from_db()
138
- self.stdout.write(
139
- self.style.ERROR(f"Attempted to update {contact!r} field(s) but failed: {updated_fields!r}: {e}")
140
- )
141
-
142
- try:
143
- contact_association = ContactAssociation(
144
- contact=contact if isinstance(contact, Contact) else None,
145
- team=contact if isinstance(contact, Team) else None,
146
- associated_object=location,
147
- role=role,
148
- status=status,
149
- )
150
- contact_association.validated_save()
151
- location.contact_name = ""
152
- location.contact_email = ""
153
- location.contact_phone = ""
154
- location.validated_save()
155
- except Exception as e:
156
- self.stdout.write(self.style.ERROR(f"Failed to create association: {e}"))
157
- else:
158
- self.stdout.write(self.style.SUCCESS(f"Associated {contact!r} to location {location.display}"))
159
-
160
- def prompt_for_role_and_status(self):
161
- # Prompt for role
162
- self.stdout.write("\nValid roles for this association:")
163
- valid_roles = list(Role.objects.get_for_model(ContactAssociation))
164
- for i, role in enumerate(valid_roles, start=1):
165
- self.stdout.write(self.style.WARNING(f"{i}") + f": {role}")
166
- while True:
167
- selected_role = input("Select a role for this association: ")
168
- if selected_role.isdigit() and 0 < int(selected_role) <= len(valid_roles):
169
- role = valid_roles[int(selected_role) - 1]
170
- break
171
-
172
- # Prompt for status
173
- self.stdout.write("\nValid statuses for this association:")
174
- valid_statuses = list(Status.objects.get_for_model(ContactAssociation))
175
- for i, status in enumerate(valid_statuses, start=1):
176
- self.stdout.write(self.style.WARNING(f"{i}") + f": {status}")
177
- while True:
178
- selected_status = input("Select a status for this association: ")
179
- if selected_status.isdigit() and 0 < int(selected_status) <= len(valid_statuses):
180
- status = valid_statuses[int(selected_status) - 1]
181
- break
182
-
183
- return role, status
184
-
185
- def create_new_contact_from_location(self, location, model):
186
- """Create a new Contact or Team from the location's contact data."""
187
-
188
- self.stdout.write(f"Creating new {model._meta.model_name.title()} for location {location.display}...")
189
- name = location.contact_name
190
- phone = location.contact_phone
191
- email = location.contact_email
192
- self.stdout.write(f" contact name: {name!r}")
193
- self.stdout.write(f" contact phone: {phone!r}")
194
- self.stdout.write(f" contact email: {email!r}")
195
-
196
- while not name:
197
- name = input(f"Name is required. Enter a name for the new {model._meta.model_name.title()}: ")
198
-
199
- try:
200
- contact = model(
201
- name=name,
202
- phone=phone,
203
- email=email,
204
- )
205
- contact.validated_save()
206
- return contact
207
- except Exception as e:
208
- self.stdout.write(self.style.ERROR(f"Failed to create {model._meta.model_name.title()}: {e}"))
209
- return None
210
-
211
- def print_contact_fields(self, contact, rjust=0):
212
- for field_name in ["phone", "email"]:
213
- if getattr(contact, field_name):
214
- self.stdout.write(
215
- field_name.title().rjust(rjust)
216
- + ": "
217
- + indent(getattr(contact, field_name), " " * (rjust + 2)).lstrip()
218
- )
@@ -1,70 +0,0 @@
1
- {% extends 'generic/object_create.html' %}
2
- {% load form_helpers %}
3
-
4
- {% block form %}
5
- <div class="panel panel-default">
6
- <div class="panel-heading"><strong>Controller</strong></div>
7
- <div class="panel-body">
8
- {% render_field form.name %}
9
- {% render_field form.status %}
10
- {% render_field form.role %}
11
- {% render_field form.location %}
12
- {% render_field form.platform %}
13
- {% render_field form.description %}
14
- </div>
15
- </div>
16
- <div class="panel panel-default">
17
- <div class="panel-heading"><strong>Integration</strong></div>
18
- <div class="panel-body">
19
- {% render_field form.external_integration %}
20
- {% with controllerdevice_tab_active=form.initial.controller_device %}
21
- <ul class="nav nav-tabs" role="tablist">
22
- <li role="presentation"{% if controllerdevice_tab_active %} class="active"{% endif %}><a href="#controllerdevice" role="tab" data-toggle="tab">Controller Device</a></li>
23
- <li role="presentation"{% if not controllerdevice_tab_active %} class="active"{% endif %}><a href="#controller_device_redundancy_group" role="tab" data-toggle="tab">Controller Device Redundancy Group</a></li>
24
- </ul>
25
- <div class="tab-content">
26
- <div class="tab-pane{% if not controllerdevice_tab_active %} active{% endif %}" id="controller_device_redundancy_group">
27
- {% render_field form.controller_device_redundancy_group %}
28
- </div>
29
- <div class="tab-pane{% if controllerdevice_tab_active %} active{% endif %}" id="controllerdevice">
30
- {% render_field form.controller_device %}
31
- </div>
32
- </div>
33
- {% endwith %}
34
- </div>
35
- </div>
36
- <div class="panel panel-default">
37
- <div class="panel-heading"><strong>Tenancy</strong></div>
38
- <div class="panel-body">
39
- {% render_field form.tenant_group %}
40
- {% render_field form.tenant %}
41
- </div>
42
- </div>
43
- {% include 'inc/extras_features_edit_form_fields.html' %}
44
- {% endblock form %}
45
-
46
- {% block javascript %}
47
- <script type="text/javascript">
48
- const controllerDevice = $('#id_controller_device');
49
- const controllerDeviceRedundancyGroup = $('#id_controller_device_redundancy_group');
50
-
51
- const controllerUpdated = function() {
52
- if (controllerDevice.val()) {
53
- controllerDeviceRedundancyGroup.val('');
54
- controllerDeviceRedundancyGroup.trigger('change');
55
- }
56
- };
57
-
58
- const controllerDeviceRedundancyGroupUpdated = function() {
59
- if (controllerDeviceRedundancyGroup.val()) {
60
- controllerDevice.val('');
61
- controllerDevice.trigger('change');
62
- }
63
- };
64
-
65
- controllerDevice.on('select2:select', controllerUpdated);
66
- controllerDevice.on('select2:unselect', controllerUpdated);
67
- controllerDeviceRedundancyGroup.on('select2:select', controllerDeviceRedundancyGroupUpdated);
68
- controllerDeviceRedundancyGroup.on('select2:unselect', controllerDeviceRedundancyGroupUpdated);
69
- </script>
70
- {% endblock %}
@@ -1,88 +0,0 @@
1
- {% extends 'generic/object_create.html' %}
2
- {% load form_helpers %}
3
- {% load helpers %}
4
- {% load static %}
5
-
6
- {% block form %}
7
- <div class="panel panel-default">
8
- <div class="panel-heading"><strong>Controller</strong></div>
9
- <div class="panel-body">
10
- {% render_field form.controller.as_hidden %}
11
- {% render_field form.controller %}
12
- {% render_field form.name %}
13
- {% render_field form.devices %}
14
- {% render_field form.parent %}
15
- {% render_field form.weight %}
16
- </div>
17
- </div>
18
- {% include 'inc/extras_features_edit_form_fields.html' %}
19
- {% endblock form %}
20
-
21
- {% block javascript %}
22
- <script type="text/javascript">
23
- const controllerUrl = '{% url 'dcim-api:controller-list' %}';
24
- const controllerGroupUrl = '{% url 'dcim-api:controllermanageddevicegroup-list' %}';
25
-
26
- const parentInput = $('select#id_parent');
27
- const controllerInput = $('select#id_controller');
28
- const hiddenControllerInput = $('input#id_controller');
29
-
30
- const request = (url, calback) => {
31
- $.ajax({
32
- url,
33
- method: "GET",
34
- success: calback,
35
- error: (error) => {
36
- const message = `Error in request. URL: ${url}`;
37
- console.error(message, error);
38
- alert(message);
39
- },
40
- });
41
- };
42
-
43
- const setController = (uid) => {
44
- console.log('Set controller:', uid);
45
- if (controllerInput.find(`option[value=${uid}]`).length) {
46
- controllerInput.val(uid);
47
- hiddenControllerInput.val(uid);
48
- } else {
49
- request(`${controllerUrl}${uid}/`, (data) => {
50
- console.log('Controller data:', data);
51
- controllerInput.append(new Option(data.name, uid));
52
- setController(uid);
53
- });
54
- }
55
- };
56
-
57
- const updateControllerFromParent = (parentUid) => {
58
- request(
59
- `${controllerGroupUrl}${parentUid}/`,
60
- (data) => data.controller && setController(data.controller.id),
61
- );
62
- };
63
-
64
- const onParentInput = (event) => {
65
- const parentUid = parentInput.val();
66
- if (parentUid) {
67
- controllerInput.prop("disabled", true);
68
- if (event) {
69
- updateControllerFromParent(parentUid);
70
- }
71
- } else {
72
- controllerInput.prop("disabled", false);
73
- }
74
- };
75
-
76
- const onControllerInput = () => {
77
- hiddenControllerInput.val(controllerInput.val());
78
- };
79
-
80
- parentInput.on('select2:select', onParentInput);
81
- parentInput.on('select2:unselect', onParentInput);
82
-
83
- controllerInput.on('select2:select', onControllerInput);
84
- controllerInput.on('select2:unselect', onControllerInput);
85
-
86
- onParentInput();
87
- </script>
88
- {% endblock %}
@@ -1,42 +0,0 @@
1
- from django.test import TestCase
2
-
3
- from nautobot.core.models.querysets import count_related
4
- from nautobot.dcim.models.locations import Location
5
- from nautobot.ipam.models import Prefix
6
- from nautobot.ipam.tables import PrefixTable
7
-
8
-
9
- class PrefixTableTestCase(TestCase):
10
- def _validate_sorted_queryset_same_with_table_queryset(self, queryset, table_class, field_name):
11
- with self.subTest(f"Assert sorting {table_class.__name__} on '{field_name}'"):
12
- table = table_class(queryset, order_by=field_name)
13
- table_queryset_data = table.data.data.values_list("pk", flat=True)
14
- sorted_queryset = queryset.order_by(field_name).values_list("pk", flat=True)
15
- self.assertEqual(list(table_queryset_data), list(sorted_queryset))
16
-
17
- def test_prefix_table_sort(self):
18
- """Assert TreeNode model table are orderable."""
19
- # Due to MySQL's lack of support for combining 'LIMIT' and 'ORDER BY' in a single query,
20
- # hence this approach.
21
- pk_list = Prefix.objects.all().values_list("pk", flat=True)[:20]
22
- pk_list = [str(pk) for pk in pk_list]
23
- queryset = Prefix.objects.filter(pk__in=pk_list)
24
-
25
- # Assets model names
26
- table_avail_fields = ["tenant", "vlan", "namespace"]
27
- for table_field_name in table_avail_fields:
28
- self._validate_sorted_queryset_same_with_table_queryset(queryset, PrefixTable, table_field_name)
29
- self._validate_sorted_queryset_same_with_table_queryset(queryset, PrefixTable, f"-{table_field_name}")
30
-
31
- # Assert `prefix`
32
- table_queryset_data = PrefixTable(queryset, order_by="prefix").data.data.values_list("pk", flat=True)
33
- prefix_queryset = queryset.order_by("network", "prefix_length").values_list("pk", flat=True)
34
- self.assertEqual(list(table_queryset_data), list(prefix_queryset))
35
- table_queryset_data = PrefixTable(queryset, order_by="-prefix").data.data.values_list("pk", flat=True)
36
- prefix_queryset = queryset.order_by("-network", "-prefix_length").values_list("pk", flat=True)
37
- self.assertEqual(list(table_queryset_data), list(prefix_queryset))
38
-
39
- # Assets `location_count`
40
- location_count_queryset = queryset.annotate(location_count=count_related(Location, "prefixes")).all()
41
- self._validate_sorted_queryset_same_with_table_queryset(location_count_queryset, PrefixTable, "location_count")
42
- self._validate_sorted_queryset_same_with_table_queryset(location_count_queryset, PrefixTable, "-location_count")