nautobot 2.4.1__py3-none-any.whl → 2.4.2__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 (406) hide show
  1. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +43 -0
  2. nautobot/circuits/tests/integration/test_relationships.py +1 -1
  3. nautobot/core/apps/__init__.py +0 -5
  4. nautobot/core/templates/generic/object_bulk_destroy.html +1 -1
  5. nautobot/core/testing/integration.py +437 -10
  6. nautobot/core/tests/test_jobs.py +34 -2
  7. nautobot/core/utils/git.py +7 -2
  8. nautobot/core/views/generic.py +1 -1
  9. nautobot/core/views/mixins.py +13 -6
  10. nautobot/core/views/utils.py +2 -2
  11. nautobot/dcim/forms.py +12 -0
  12. nautobot/dcim/tables/devices.py +2 -1
  13. nautobot/dcim/templates/dcim/cable.html +1 -1
  14. nautobot/dcim/templates/dcim/device/base.html +1 -1
  15. nautobot/dcim/templates/dcim/device.html +2 -2
  16. nautobot/dcim/templates/dcim/device_component.html +1 -1
  17. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  18. nautobot/dcim/templates/dcim/location.html +1 -1
  19. nautobot/dcim/templates/dcim/locationtype.html +1 -1
  20. nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
  21. nautobot/dcim/templates/dcim/manufacturer.html +1 -1
  22. nautobot/dcim/templates/dcim/platform.html +1 -1
  23. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  24. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  25. nautobot/dcim/templates/dcim/rack.html +1 -1
  26. nautobot/dcim/templates/dcim/rackgroup.html +1 -1
  27. nautobot/dcim/templates/dcim/rackreservation.html +2 -2
  28. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  29. nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
  30. nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
  31. nautobot/dcim/tests/test_views.py +9 -1
  32. nautobot/dcim/views.py +12 -15
  33. nautobot/extras/api/serializers.py +33 -0
  34. nautobot/extras/api/views.py +11 -3
  35. nautobot/extras/constants.py +1 -0
  36. nautobot/extras/datasources/git.py +125 -0
  37. nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
  38. nautobot/extras/models/customfields.py +29 -12
  39. nautobot/extras/models/datasources.py +85 -0
  40. nautobot/extras/models/models.py +15 -0
  41. nautobot/extras/models/relationships.py +17 -5
  42. nautobot/extras/signals.py +15 -1
  43. nautobot/extras/templates/extras/computedfield.html +1 -1
  44. nautobot/extras/templates/extras/configcontext.html +1 -1
  45. nautobot/extras/templates/extras/configcontextschema.html +1 -1
  46. nautobot/extras/templates/extras/customfield.html +1 -1
  47. nautobot/extras/templates/extras/customlink.html +1 -1
  48. nautobot/extras/templates/extras/dynamicgroup.html +1 -1
  49. nautobot/extras/templates/extras/exporttemplate.html +1 -1
  50. nautobot/extras/templates/extras/gitrepository.html +1 -1
  51. nautobot/extras/templates/extras/graphqlquery.html +1 -1
  52. nautobot/extras/templates/extras/job_detail.html +1 -1
  53. nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
  54. nautobot/extras/templates/extras/jobhook.html +1 -1
  55. nautobot/extras/templates/extras/jobresult.html +1 -1
  56. nautobot/extras/templates/extras/objectchange.html +1 -1
  57. nautobot/extras/templates/extras/plugin_detail.html +1 -1
  58. nautobot/extras/templates/extras/relationship.html +1 -63
  59. nautobot/extras/templates/extras/role_retrieve.html +1 -1
  60. nautobot/extras/templates/extras/scheduledjob.html +1 -1
  61. nautobot/extras/templates/extras/secret.html +1 -1
  62. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  63. nautobot/extras/templates/extras/status.html +1 -1
  64. nautobot/extras/templates/extras/tag.html +1 -1
  65. nautobot/extras/templates/extras/webhook.html +1 -1
  66. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
  67. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
  68. nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
  69. nautobot/extras/tests/git_helper.py +9 -1
  70. nautobot/extras/tests/integration/__init__.py +29 -16
  71. nautobot/extras/tests/test_api.py +6 -0
  72. nautobot/extras/tests/test_customfields.py +49 -51
  73. nautobot/extras/tests/test_datasources.py +27 -0
  74. nautobot/extras/tests/test_models.py +283 -0
  75. nautobot/extras/tests/test_utils.py +22 -1
  76. nautobot/extras/utils.py +17 -8
  77. nautobot/extras/views.py +55 -12
  78. nautobot/ipam/models.py +8 -2
  79. nautobot/ipam/tables.py +2 -2
  80. nautobot/ipam/templates/ipam/ipaddress.html +1 -1
  81. nautobot/ipam/templates/ipam/prefix.html +1 -1
  82. nautobot/ipam/templates/ipam/rir.html +1 -1
  83. nautobot/ipam/templates/ipam/routetarget.html +1 -1
  84. nautobot/ipam/templates/ipam/service.html +1 -1
  85. nautobot/ipam/templates/ipam/vlan.html +1 -1
  86. nautobot/ipam/templates/ipam/vlangroup.html +1 -1
  87. nautobot/ipam/templates/ipam/vrf.html +1 -1
  88. nautobot/ipam/tests/test_models.py +24 -0
  89. nautobot/ipam/tests/test_utils.py +41 -2
  90. nautobot/ipam/utils/__init__.py +18 -11
  91. nautobot/project-static/docs/404.html +87 -12
  92. nautobot/project-static/docs/apps/index.html +87 -12
  93. nautobot/project-static/docs/apps/nautobot-apps.html +87 -12
  94. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.60a45f97.min.js} +1 -1
  95. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.60a45f97.min.js.map} +1 -1
  96. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
  97. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
  98. nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
  99. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +87 -12
  100. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +87 -12
  101. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +87 -12
  102. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +87 -12
  103. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +87 -12
  104. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +87 -12
  105. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +87 -12
  106. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +87 -12
  107. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +87 -12
  108. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +87 -12
  109. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +87 -12
  110. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +87 -12
  111. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +87 -12
  112. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +87 -12
  113. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +87 -12
  114. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +87 -12
  115. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +87 -12
  116. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +87 -12
  117. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +87 -12
  118. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +87 -12
  119. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +87 -12
  120. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +87 -12
  121. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +177 -20
  122. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +114 -17
  123. nautobot/project-static/docs/development/apps/api/configuration-view.html +87 -12
  124. nautobot/project-static/docs/development/apps/api/database-backend-config.html +87 -12
  125. nautobot/project-static/docs/development/apps/api/models/django-admin.html +87 -12
  126. nautobot/project-static/docs/development/apps/api/models/global-search.html +87 -12
  127. nautobot/project-static/docs/development/apps/api/models/graphql.html +87 -12
  128. nautobot/project-static/docs/development/apps/api/models/index.html +87 -12
  129. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +87 -12
  130. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +87 -12
  131. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +87 -12
  132. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +87 -12
  133. nautobot/project-static/docs/development/apps/api/platform-features/index.html +87 -12
  134. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +87 -12
  135. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +87 -12
  136. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +87 -12
  137. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +87 -12
  138. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +87 -12
  139. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +87 -12
  140. nautobot/project-static/docs/development/apps/api/prometheus.html +87 -12
  141. nautobot/project-static/docs/development/apps/api/setup.html +87 -12
  142. nautobot/project-static/docs/development/apps/api/testing.html +87 -12
  143. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +87 -12
  144. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +87 -12
  145. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +87 -12
  146. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +87 -12
  147. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +87 -12
  148. nautobot/project-static/docs/development/apps/api/views/base-template.html +87 -12
  149. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +87 -12
  150. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +87 -12
  151. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +87 -12
  152. nautobot/project-static/docs/development/apps/api/views/index.html +87 -12
  153. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +87 -12
  154. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +87 -12
  155. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +87 -12
  156. nautobot/project-static/docs/development/apps/api/views/notes.html +87 -12
  157. nautobot/project-static/docs/development/apps/api/views/rest-api.html +87 -12
  158. nautobot/project-static/docs/development/apps/api/views/urls.html +87 -12
  159. nautobot/project-static/docs/development/apps/index.html +87 -12
  160. nautobot/project-static/docs/development/apps/migration/code-updates.html +87 -12
  161. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +87 -12
  162. nautobot/project-static/docs/development/apps/migration/from-v1.html +87 -12
  163. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +87 -12
  164. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +87 -12
  165. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +87 -12
  166. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +87 -12
  167. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +87 -12
  168. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +87 -12
  169. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +88 -13
  170. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +87 -12
  171. nautobot/project-static/docs/development/apps/porting-from-netbox.html +87 -12
  172. nautobot/project-static/docs/development/core/application-registry.html +87 -12
  173. nautobot/project-static/docs/development/core/best-practices.html +87 -12
  174. nautobot/project-static/docs/development/core/bootstrap-ui.html +87 -12
  175. nautobot/project-static/docs/development/core/caching.html +87 -12
  176. nautobot/project-static/docs/development/core/controllers.html +87 -12
  177. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +87 -12
  178. nautobot/project-static/docs/development/core/generic-views.html +87 -12
  179. nautobot/project-static/docs/development/core/getting-started.html +87 -12
  180. nautobot/project-static/docs/development/core/homepage.html +87 -12
  181. nautobot/project-static/docs/development/core/index.html +87 -12
  182. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +87 -12
  183. nautobot/project-static/docs/development/core/model-checklist.html +87 -12
  184. nautobot/project-static/docs/development/core/model-features.html +87 -12
  185. nautobot/project-static/docs/development/core/natural-keys.html +87 -12
  186. nautobot/project-static/docs/development/core/navigation-menu.html +87 -12
  187. nautobot/project-static/docs/development/core/release-checklist.html +87 -12
  188. nautobot/project-static/docs/development/core/role-internals.html +87 -12
  189. nautobot/project-static/docs/development/core/settings.html +87 -12
  190. nautobot/project-static/docs/development/core/style-guide.html +87 -12
  191. nautobot/project-static/docs/development/core/templates.html +88 -13
  192. nautobot/project-static/docs/development/core/testing.html +87 -12
  193. nautobot/project-static/docs/development/core/ui-component-framework.html +87 -12
  194. nautobot/project-static/docs/development/core/user-preferences.html +87 -12
  195. nautobot/project-static/docs/development/index.html +87 -12
  196. nautobot/project-static/docs/development/jobs/index.html +87 -12
  197. nautobot/project-static/docs/development/jobs/migration/from-v1.html +87 -12
  198. nautobot/project-static/docs/index.html +87 -12
  199. nautobot/project-static/docs/overview/application_stack.html +87 -12
  200. nautobot/project-static/docs/overview/design_philosophy.html +87 -12
  201. nautobot/project-static/docs/release-notes/index.html +87 -12
  202. nautobot/project-static/docs/release-notes/version-1.0.html +87 -12
  203. nautobot/project-static/docs/release-notes/version-1.1.html +87 -12
  204. nautobot/project-static/docs/release-notes/version-1.2.html +87 -12
  205. nautobot/project-static/docs/release-notes/version-1.3.html +87 -12
  206. nautobot/project-static/docs/release-notes/version-1.4.html +87 -12
  207. nautobot/project-static/docs/release-notes/version-1.5.html +87 -12
  208. nautobot/project-static/docs/release-notes/version-1.6.html +87 -12
  209. nautobot/project-static/docs/release-notes/version-2.0.html +87 -12
  210. nautobot/project-static/docs/release-notes/version-2.1.html +87 -12
  211. nautobot/project-static/docs/release-notes/version-2.2.html +87 -12
  212. nautobot/project-static/docs/release-notes/version-2.3.html +87 -12
  213. nautobot/project-static/docs/release-notes/version-2.4.html +277 -12
  214. nautobot/project-static/docs/requirements.txt +1 -1
  215. nautobot/project-static/docs/search/search_index.json +1 -1
  216. nautobot/project-static/docs/sitemap.xml +296 -288
  217. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  218. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +87 -12
  219. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +87 -12
  220. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +87 -12
  221. nautobot/project-static/docs/user-guide/administration/configuration/index.html +87 -12
  222. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +87 -12
  223. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +87 -12
  224. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +87 -12
  225. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +90 -15
  226. nautobot/project-static/docs/user-guide/administration/guides/docker.html +87 -12
  227. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +87 -12
  228. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +87 -12
  229. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +87 -12
  230. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +87 -12
  231. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +87 -12
  232. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +87 -12
  233. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +87 -12
  234. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +87 -12
  235. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +87 -12
  236. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +87 -12
  237. nautobot/project-static/docs/user-guide/administration/installation/index.html +87 -12
  238. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +87 -12
  239. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +87 -12
  240. nautobot/project-static/docs/user-guide/administration/installation/services.html +87 -12
  241. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +87 -12
  242. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +87 -12
  243. nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
  244. nautobot/project-static/docs/user-guide/administration/security/notices.html +9843 -0
  245. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +87 -12
  246. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +87 -12
  247. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +87 -12
  248. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +87 -12
  249. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +87 -12
  250. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +87 -12
  251. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +87 -12
  252. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +87 -12
  253. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +87 -12
  254. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +87 -12
  255. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +87 -12
  256. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +87 -12
  257. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +87 -12
  258. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +87 -12
  259. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +87 -12
  260. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +87 -12
  261. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +87 -12
  262. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +87 -12
  263. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +87 -12
  264. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +87 -12
  265. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +87 -12
  266. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +87 -12
  267. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +87 -12
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +87 -12
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +87 -12
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +87 -12
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +87 -12
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +87 -12
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +87 -12
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +87 -12
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +87 -12
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +87 -12
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +87 -12
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +87 -12
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +87 -12
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +87 -12
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +87 -12
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +87 -12
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +87 -12
  284. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +87 -12
  285. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +87 -12
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +87 -12
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +87 -12
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +87 -12
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +87 -12
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +87 -12
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +87 -12
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +87 -12
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +87 -12
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +87 -12
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +87 -12
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +87 -12
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +87 -12
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +87 -12
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +87 -12
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +87 -12
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +87 -12
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +87 -12
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +87 -12
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +87 -12
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +87 -12
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +87 -12
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +87 -12
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +87 -12
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +87 -12
  310. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +87 -12
  311. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +87 -12
  312. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +87 -12
  313. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +87 -12
  314. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +87 -12
  315. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +87 -12
  316. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +87 -12
  317. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +87 -12
  318. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +87 -12
  319. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +87 -12
  320. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +87 -12
  321. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +87 -12
  322. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +87 -12
  323. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +87 -12
  324. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +87 -12
  325. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +87 -12
  326. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +87 -12
  327. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +87 -12
  328. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +87 -12
  329. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +87 -12
  330. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +87 -12
  331. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +87 -12
  332. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +87 -12
  333. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +87 -12
  334. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +87 -12
  335. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +87 -12
  336. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +87 -12
  337. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +87 -12
  338. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +87 -12
  339. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +90 -15
  340. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +87 -12
  341. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +87 -12
  342. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +87 -12
  343. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +87 -12
  344. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +87 -12
  345. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +87 -12
  346. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +187 -29
  347. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +87 -12
  348. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +87 -12
  349. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +87 -12
  350. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +87 -12
  351. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +87 -12
  352. nautobot/project-static/docs/user-guide/index.html +87 -12
  353. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +87 -12
  354. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +87 -12
  355. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +87 -12
  356. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +87 -12
  357. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +87 -12
  358. nautobot/project-static/docs/user-guide/platform-functionality/events.html +87 -12
  359. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +87 -12
  360. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +87 -12
  361. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +407 -14
  362. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +87 -12
  363. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +87 -12
  364. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +87 -12
  365. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +87 -12
  366. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +87 -12
  367. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +87 -12
  368. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +87 -12
  369. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +87 -12
  370. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +87 -12
  371. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +87 -12
  372. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +87 -12
  373. nautobot/project-static/docs/user-guide/platform-functionality/note.html +87 -12
  374. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +87 -12
  375. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +87 -12
  376. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +87 -12
  377. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +87 -12
  378. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +87 -12
  379. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +87 -12
  380. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +87 -12
  381. nautobot/project-static/docs/user-guide/platform-functionality/role.html +87 -12
  382. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +87 -12
  383. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +87 -12
  384. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +87 -12
  385. nautobot/project-static/docs/user-guide/platform-functionality/status.html +87 -12
  386. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +87 -12
  387. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +87 -12
  388. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +87 -12
  389. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +87 -12
  390. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +87 -12
  391. nautobot/tenancy/templates/tenancy/tenant.html +1 -2
  392. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  393. nautobot/virtualization/templates/virtualization/cluster.html +1 -1
  394. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
  395. nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
  396. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  397. nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
  398. {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/METADATA +5 -5
  399. {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/RECORD +404 -397
  400. nautobot/dcim/tests/integration/test_device_bulk_delete.py +0 -189
  401. nautobot/dcim/tests/integration/test_device_bulk_edit.py +0 -181
  402. /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
  403. {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/LICENSE.txt +0 -0
  404. {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/NOTICE +0 -0
  405. {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/WHEEL +0 -0
  406. {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/entry_points.txt +0 -0
@@ -4,13 +4,34 @@ import uuid
4
4
  from django.core.cache import cache
5
5
 
6
6
  from nautobot.core.testing import TestCase
7
+ from nautobot.dcim.models import Device, LocationType
7
8
  from nautobot.extras.choices import JobQueueTypeChoices
8
9
  from nautobot.extras.models import JobQueue
9
10
  from nautobot.extras.registry import registry
10
- from nautobot.extras.utils import get_celery_queues, get_worker_count, populate_model_features_registry
11
+ from nautobot.extras.utils import (
12
+ get_base_template,
13
+ get_celery_queues,
14
+ get_worker_count,
15
+ populate_model_features_registry,
16
+ )
17
+ from nautobot.users.models import Token
11
18
 
12
19
 
13
20
  class UtilsTestCase(TestCase):
21
+ def test_get_base_template(self):
22
+ with self.subTest("explicitly specified base_template always wins"):
23
+ self.assertEqual(get_base_template("dcim/device/base.html", Device), "dcim/device/base.html")
24
+
25
+ with self.subTest("<model>.html wins over <model>_retrieve.html"):
26
+ # TODO: why do we even have both locationtype.html and locationtype_retrieve.html?
27
+ self.assertEqual(get_base_template(None, LocationType), "dcim/locationtype.html")
28
+
29
+ with self.subTest("<model>_retrieve.html is used if present"):
30
+ self.assertEqual(get_base_template(None, JobQueue), "extras/jobqueue_retrieve.html")
31
+
32
+ with self.subTest("generic/object_retrieve.html is used as a fallback"):
33
+ self.assertEqual(get_base_template(None, Token), "generic/object_retrieve.html")
34
+
14
35
  @mock.patch("celery.app.control.Inspect.active_queues")
15
36
  def test_get_celery_queues(self, mock_active_queues):
16
37
  with self.subTest("No queues"):
nautobot/extras/utils.py CHANGED
@@ -6,6 +6,7 @@ import hmac
6
6
  import logging
7
7
  import re
8
8
  import sys
9
+ from typing import Optional
9
10
 
10
11
  from django.apps import apps
11
12
  from django.conf import settings
@@ -13,7 +14,7 @@ from django.contrib.contenttypes.models import ContentType
13
14
  from django.core.cache import cache
14
15
  from django.core.validators import ValidationError
15
16
  from django.db import transaction
16
- from django.db.models import Q
17
+ from django.db.models import Model, Q
17
18
  from django.template.loader import get_template, TemplateDoesNotExist
18
19
  from django.utils.deconstruct import deconstructible
19
20
  import kubernetes.client
@@ -36,16 +37,24 @@ from nautobot.extras.registry import registry
36
37
  logger = logging.getLogger(__name__)
37
38
 
38
39
 
39
- def get_base_template(base_template, model):
40
+ def get_base_template(base_template: Optional[str], model: type[Model]) -> str:
40
41
  """
41
- Returns the name of the base template, if the base_template is not None
42
- Otherwise, default to using "<app>/<model>.html" as the base template, if it exists.
43
- Otherwise, check if "<app>/<model>_retrieve.html" used in `NautobotUIViewSet` exists.
44
- If both templates do not exist, fall back to "base.html".
42
+ Attempt to locate the correct base template for an object detail view and related views, if one was not specified.
43
+
44
+ Args:
45
+ base_template (str, optional): If not None, this explicitly specified template will be preferred.
46
+ model (Model): The model to identify a base template for, if base_template is None.
47
+
48
+ Returns the specified `base_template`, if not `None`.
49
+ Otherwise, if `"<app>/<model_name>.html"` exists (legacy ObjectView pattern), returns that string.
50
+ Otherwise, if `"<app>/<model_name>_retrieve.html"` exists (as used in `NautobotUIViewSet`), returns that string.
51
+ If all else fails, returns `"generic/object_retrieve.html"`.
52
+
53
+ Note: before Nautobot 2.4.2, this API would default to "base.html" rather than "generic/object_retrieve.html".
54
+ This behavior was changed to the current behavior to address issue #6550 and similar incorrect behavior.
45
55
  """
46
56
  if base_template is None:
47
57
  base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
48
- # 2.0 TODO(Hanlin): This can be removed once an object view has been established for every model.
49
58
  try:
50
59
  get_template(base_template)
51
60
  except TemplateDoesNotExist:
@@ -53,7 +62,7 @@ def get_base_template(base_template, model):
53
62
  try:
54
63
  get_template(base_template)
55
64
  except TemplateDoesNotExist:
56
- base_template = "base.html"
65
+ base_template = "generic/object_retrieve.html"
57
66
  return base_template
58
67
 
59
68
 
nautobot/extras/views.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from typing import Optional
2
3
  from urllib.parse import parse_qs
3
4
 
4
5
  from django.contrib import messages
@@ -31,6 +32,7 @@ from nautobot.core.models.utils import pretty_print_query, serialize_object_v2
31
32
  from nautobot.core.tables import ButtonsColumn
32
33
  from nautobot.core.ui import object_detail
33
34
  from nautobot.core.ui.choices import SectionChoices
35
+ from nautobot.core.ui.object_detail import ObjectDetailContent, ObjectFieldsPanel
34
36
  from nautobot.core.utils.config import get_settings_or_config
35
37
  from nautobot.core.utils.lookup import (
36
38
  get_filterset_for_model,
@@ -900,10 +902,13 @@ class DynamicGroupBulkDeleteView(generic.BulkDeleteView):
900
902
  class ObjectDynamicGroupsView(generic.GenericView):
901
903
  """
902
904
  Present a list of dynamic groups associated to a particular object.
903
- base_template: The name of the template to extend. If not provided, "<app>/<model>.html" will be used.
905
+
906
+ base_template: Specify to explicitly identify the base object detail template to render.
907
+ If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
908
+ will be used, as per `get_base_template()`.
904
909
  """
905
910
 
906
- base_template = None
911
+ base_template: Optional[str] = None
907
912
 
908
913
  def get(self, request, model, **kwargs):
909
914
  # Handle QuerySet restriction of parent object if needed
@@ -926,7 +931,7 @@ class ObjectDynamicGroupsView(generic.GenericView):
926
931
  }
927
932
  RequestConfig(request, paginate).configure(dynamicgroups_table)
928
933
 
929
- self.base_template = get_base_template(self.base_template, model)
934
+ base_template = get_base_template(self.base_template, model)
930
935
 
931
936
  return render(
932
937
  request,
@@ -936,7 +941,7 @@ class ObjectDynamicGroupsView(generic.GenericView):
936
941
  "verbose_name": obj._meta.verbose_name,
937
942
  "verbose_name_plural": obj._meta.verbose_name_plural,
938
943
  "table": dynamicgroups_table,
939
- "base_template": self.base_template,
944
+ "base_template": base_template,
940
945
  "active_tab": "dynamic-groups",
941
946
  },
942
947
  )
@@ -2185,10 +2190,13 @@ class ObjectChangeView(generic.ObjectView):
2185
2190
  class ObjectChangeLogView(generic.GenericView):
2186
2191
  """
2187
2192
  Present a history of changes made to a particular object.
2188
- base_template: The name of the template to extend. If not provided, "<app>/<model>.html" will be used.
2193
+
2194
+ base_template: Specify to explicitly identify the base object detail template to render.
2195
+ If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
2196
+ will be used, as per `get_base_template()`.
2189
2197
  """
2190
2198
 
2191
- base_template = None
2199
+ base_template: Optional[str] = None
2192
2200
 
2193
2201
  def get(self, request, model, **kwargs):
2194
2202
  # Handle QuerySet restriction of parent object if needed
@@ -2216,7 +2224,7 @@ class ObjectChangeLogView(generic.GenericView):
2216
2224
  }
2217
2225
  RequestConfig(request, paginate).configure(objectchanges_table)
2218
2226
 
2219
- self.base_template = get_base_template(self.base_template, model)
2227
+ base_template = get_base_template(self.base_template, model)
2220
2228
 
2221
2229
  return render(
2222
2230
  request,
@@ -2226,7 +2234,7 @@ class ObjectChangeLogView(generic.GenericView):
2226
2234
  "verbose_name": obj._meta.verbose_name,
2227
2235
  "verbose_name_plural": obj._meta.verbose_name_plural,
2228
2236
  "table": objectchanges_table,
2229
- "base_template": self.base_template,
2237
+ "base_template": base_template,
2230
2238
  "active_tab": "changelog",
2231
2239
  },
2232
2240
  )
@@ -2319,10 +2327,13 @@ class NoteDeleteView(generic.ObjectDeleteView):
2319
2327
  class ObjectNotesView(generic.GenericView):
2320
2328
  """
2321
2329
  Present a list of notes associated to a particular object.
2322
- base_template: The name of the template to extend. If not provided, "<app>/<model>.html" will be used.
2330
+
2331
+ base_template: Specify to explicitly identify the base object detail template to render.
2332
+ If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
2333
+ will be used, as per `get_base_template()`.
2323
2334
  """
2324
2335
 
2325
- base_template = None
2336
+ base_template: Optional[str] = None
2326
2337
 
2327
2338
  def get(self, request, model, **kwargs):
2328
2339
  # Handle QuerySet restriction of parent object if needed
@@ -2346,7 +2357,7 @@ class ObjectNotesView(generic.GenericView):
2346
2357
  }
2347
2358
  RequestConfig(request, paginate).configure(notes_table)
2348
2359
 
2349
- self.base_template = get_base_template(self.base_template, model)
2360
+ base_template = get_base_template(self.base_template, model)
2350
2361
 
2351
2362
  return render(
2352
2363
  request,
@@ -2356,7 +2367,7 @@ class ObjectNotesView(generic.GenericView):
2356
2367
  "verbose_name": obj._meta.verbose_name,
2357
2368
  "verbose_name_plural": obj._meta.verbose_name_plural,
2358
2369
  "table": notes_table,
2359
- "base_template": self.base_template,
2370
+ "base_template": base_template,
2360
2371
  "active_tab": "notes",
2361
2372
  "form": notes_form,
2362
2373
  },
@@ -2378,6 +2389,38 @@ class RelationshipListView(generic.ObjectListView):
2378
2389
 
2379
2390
  class RelationshipView(generic.ObjectView):
2380
2391
  queryset = Relationship.objects.all()
2392
+ object_detail_content = ObjectDetailContent(
2393
+ panels=(
2394
+ ObjectFieldsPanel(
2395
+ label="Relationship",
2396
+ section=SectionChoices.LEFT_HALF,
2397
+ weight=100,
2398
+ fields="__all__",
2399
+ exclude_fields=[
2400
+ "source_type",
2401
+ "source_label",
2402
+ "source_hidden",
2403
+ "source_filter",
2404
+ "destination_type",
2405
+ "destination_label",
2406
+ "destination_hidden",
2407
+ "destination_filter",
2408
+ ],
2409
+ ),
2410
+ ObjectFieldsPanel(
2411
+ label="Source Attributes",
2412
+ section=SectionChoices.RIGHT_HALF,
2413
+ weight=100,
2414
+ fields=["source_type", "source_label", "source_hidden", "source_filter"],
2415
+ ),
2416
+ ObjectFieldsPanel(
2417
+ label="Destination Attributes",
2418
+ section=SectionChoices.RIGHT_HALF,
2419
+ weight=200,
2420
+ fields=["destination_type", "destination_label", "destination_hidden", "destination_filter"],
2421
+ ),
2422
+ )
2423
+ )
2381
2424
 
2382
2425
 
2383
2426
  class RelationshipEditView(generic.ObjectEditView):
nautobot/ipam/models.py CHANGED
@@ -975,7 +975,10 @@ class Prefix(PrimaryModel):
975
975
  while `<Prefix 10.0.0.0/16>.get_all_ips()` will return *both* 10.0.0.1.24 and 10.0.1.1/24.
976
976
  """
977
977
  return IPAddress.objects.filter(
978
- parent__namespace=self.namespace, host__gte=self.network, host__lte=self.broadcast
978
+ parent__namespace=self.namespace,
979
+ ip_version=self.ip_version,
980
+ host__gte=self.network,
981
+ host__lte=self.broadcast,
979
982
  )
980
983
 
981
984
  def get_first_available_prefix(self):
@@ -1016,7 +1019,10 @@ class Prefix(PrimaryModel):
1016
1019
  # change this when that is the case, see #3873 for historical context.
1017
1020
  if self.type != choices.PrefixTypeChoices.TYPE_CONTAINER:
1018
1021
  pool_ips = IPAddress.objects.filter(
1019
- parent__namespace=self.namespace, host__gte=self.network, host__lte=self.broadcast
1022
+ parent__namespace=self.namespace,
1023
+ ip_version=self.ip_version,
1024
+ host__gte=self.network,
1025
+ host__lte=self.broadcast,
1020
1026
  ).values_list("host", flat=True)
1021
1027
  child_ips = netaddr.IPSet(pool_ips)
1022
1028
 
nautobot/ipam/tables.py CHANGED
@@ -79,7 +79,7 @@ IPADDRESS_LINK = """
79
79
  {% elif perms.ipam.add_ipaddress %}
80
80
  <a href="\
81
81
  {% url 'ipam:ipaddress_add' %}\
82
- ?address={{ record.1 }}\
82
+ ?address={{ record.1 }}&namespace={{ object.namespace.pk }}\
83
83
  {% if object.vrf %}&vrf={{ object.vrf.pk }}{% endif %}\
84
84
  {% if object.tenant %}&tenant={{ object.tenant.pk }}{% endif %}\
85
85
  " class="btn btn-xs btn-success">\
@@ -101,7 +101,7 @@ IPADDRESS_COPY_LINK = """
101
101
  {% elif perms.ipam.add_ipaddress %}
102
102
  <a href="\
103
103
  {% url 'ipam:ipaddress_add' %}\
104
- ?address={{ record.1 }}\
104
+ ?address={{ record.1 }}&namespace={{ object.namespace.pk }}\
105
105
  {% if object.vrf %}&vrf={{ object.vrf.pk }}{% endif %}\
106
106
  {% if object.tenant %}&tenant={{ object.tenant.pk }}{% endif %}\
107
107
  " class="btn btn-xs btn-success">\
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
  {% load render_table from django_tables2 %}
4
4
 
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block breadcrumbs %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}
@@ -1 +1 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block extra_breadcrumbs %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block extra_breadcrumbs %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load buttons %}
3
3
  {% load helpers %}
4
4
  {% load plugins %}
@@ -1 +1 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
@@ -827,6 +827,24 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
827
827
 
828
828
  self.assertEqual(parent_prefix.get_first_available_ip(), "10.0.3.2/29")
829
829
 
830
+ def test_get_all_ips_issue_3319(self):
831
+ # https://github.com/nautobot/nautobot/issues/3319
832
+ # Confirm that IPv4 addresses aren't caught up in the IPv6 ::/96 subnet by accident, and vice versa.
833
+ prefix_v6 = Prefix.objects.create(
834
+ prefix="::/0", type=PrefixTypeChoices.TYPE_CONTAINER, status=self.status, namespace=self.namespace
835
+ )
836
+ prefix_v4 = Prefix.objects.create(
837
+ prefix="0.0.0.0/0", type=PrefixTypeChoices.TYPE_CONTAINER, status=self.status, namespace=self.namespace
838
+ )
839
+ IPAddress.objects.create(address="::0102:0304/128", status=self.status, namespace=self.namespace)
840
+ IPAddress.objects.create(address="1.2.3.4/32", status=self.status, namespace=self.namespace)
841
+ self.assertQuerysetEqualAndNotEmpty(
842
+ prefix_v6.get_all_ips(), IPAddress.objects.filter(ip_version=6, parent__namespace=self.namespace)
843
+ )
844
+ self.assertQuerysetEqualAndNotEmpty(
845
+ prefix_v4.get_all_ips(), IPAddress.objects.filter(ip_version=4, parent__namespace=self.namespace)
846
+ )
847
+
830
848
  def test_get_utilization(self):
831
849
  # Container Prefix
832
850
  prefix = Prefix.objects.create(
@@ -982,6 +1000,12 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
982
1000
  Prefix.objects.create(prefix="ab80::/9", status=self.status, namespace=self.namespace)
983
1001
  self.assertEqual(large_prefix_v6.get_utilization(), (2**120, 2**120))
984
1002
 
1003
+ # https://github.com/nautobot/nautobot/issues/3319
1004
+ v4_10dot_address_space_in_v6 = Prefix.objects.create(
1005
+ prefix="0a00::/8", type=PrefixTypeChoices.TYPE_NETWORK, status=self.status, namespace=self.namespace
1006
+ )
1007
+ self.assertSequenceEqual(v4_10dot_address_space_in_v6.get_utilization(), (0, 2**120))
1008
+
985
1009
  #
986
1010
  # Uniqueness enforcement tests
987
1011
  #
@@ -1,9 +1,10 @@
1
1
  from django.test import TestCase
2
+ import netaddr
2
3
 
3
4
  from nautobot.core.forms.utils import parse_numeric_range
4
5
  from nautobot.extras.models import Status
5
- from nautobot.ipam.models import VLAN, VLANGroup
6
- from nautobot.ipam.utils import add_available_vlans
6
+ from nautobot.ipam.models import IPAddress, Namespace, Prefix, VLAN, VLANGroup
7
+ from nautobot.ipam.utils import add_available_ipaddresses, add_available_vlans
7
8
 
8
9
 
9
10
  class AddAvailableVlansTest(TestCase):
@@ -27,6 +28,44 @@ class AddAvailableVlansTest(TestCase):
27
28
  )
28
29
 
29
30
 
31
+ class AddAvailableIPsTest(TestCase):
32
+ """Tests for add_available_ipaddresses()."""
33
+
34
+ def test_add_available_ipaddresses_ipv4(self):
35
+ prefix = Prefix.objects.create(prefix="22.22.22.0/24", status=Status.objects.get_for_model(Prefix).first())
36
+ ip_status = Status.objects.get_for_model(IPAddress).first()
37
+ # .0 isn't available since this isn't a Pool prefix
38
+ available_1 = (9, "22.22.22.1/24")
39
+ ip_1 = IPAddress.objects.create(address="22.22.22.10/24", status=ip_status)
40
+ available_2 = (10, "22.22.22.11/24")
41
+ ip_2 = IPAddress.objects.create(address="22.22.22.21/24", status=ip_status)
42
+ available_3 = (233, "22.22.22.22/24")
43
+ # .255 isn't available since this isn't a Pool prefix
44
+ self.assertEqual(
45
+ add_available_ipaddresses(prefix=netaddr.IPNetwork(prefix.prefix), ipaddress_list=(ip_1, ip_2)),
46
+ [available_1, ip_1, available_2, ip_2, available_3],
47
+ )
48
+
49
+ def test_add_available_ipaddresses_ipv6(self):
50
+ namespace = Namespace.objects.create(name="add_available_ipv6")
51
+ prefix = Prefix.objects.create(
52
+ prefix="::/0", status=Status.objects.get_for_model(Prefix).first(), namespace=namespace
53
+ )
54
+ ip_status = Status.objects.get_for_model(IPAddress).first()
55
+ # .0 is available in IPv6
56
+ available_1 = (10, "::/0")
57
+ ip_1 = IPAddress.objects.create(address="::a/0", status=ip_status, namespace=namespace)
58
+ available_2 = (2**128 - 10 - 10 - 2, "::b/0")
59
+ ip_2 = IPAddress.objects.create(
60
+ address="ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff5/0", status=ip_status, namespace=namespace
61
+ )
62
+ available_3 = (10, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff6/0")
63
+ self.assertEqual(
64
+ add_available_ipaddresses(prefix=netaddr.IPNetwork(prefix.prefix), ipaddress_list=(ip_1, ip_2)),
65
+ [available_1, ip_1, available_2, ip_2, available_3],
66
+ )
67
+
68
+
30
69
  class ParseNumericRangeTest(TestCase):
31
70
  """Tests for add_available_vlans()."""
32
71
 
@@ -41,20 +41,27 @@ def get_add_available_prefixes_callback(show_available: bool, parent: Prefix):
41
41
 
42
42
  def add_available_ipaddresses(prefix: netaddr.IPNetwork, ipaddress_list: Iterable[IPAddress], is_pool: bool = False):
43
43
  """
44
- Annotate ranges of available IP addresses within a given prefix. If is_pool is True, the first and last IP will be
45
- considered usable (regardless of mask length).
46
- """
44
+ Annotate ranges of available IP addresses within a given prefix.
45
+
46
+ Args:
47
+ prefix (netaddr.IPNetwork): The network to calculate available addresses within.
48
+ ipaddress_list (Iterable[IPAddress]): List or QuerySet of extant IPAddress objects.
49
+ is_pool (bool): If True, the first/last IPs in the prefix will be considered usable, regardless of mask length.
47
50
 
51
+ Returns:
52
+ The contents of `ipaddress_list` interleaved with tuples of the form
53
+ `(number_of_available_addresses, first_such_address)`.
54
+ """
48
55
  output = []
49
56
  prev_ip = None
50
57
 
51
58
  # Ignore the network and broadcast addresses for non-pool IPv4 prefixes larger than /31.
52
59
  if prefix.version == 4 and prefix.prefixlen < 31 and not is_pool:
53
- first_ip_in_prefix = netaddr.IPAddress(prefix.first + 1)
54
- last_ip_in_prefix = netaddr.IPAddress(prefix.last - 1)
60
+ first_ip_in_prefix = netaddr.IPAddress(prefix.first + 1, version=prefix.version)
61
+ last_ip_in_prefix = netaddr.IPAddress(prefix.last - 1, version=prefix.version)
55
62
  else:
56
- first_ip_in_prefix = netaddr.IPAddress(prefix.first)
57
- last_ip_in_prefix = netaddr.IPAddress(prefix.last)
63
+ first_ip_in_prefix = netaddr.IPAddress(prefix.first, version=prefix.version)
64
+ last_ip_in_prefix = netaddr.IPAddress(prefix.last, version=prefix.version)
58
65
 
59
66
  if not ipaddress_list:
60
67
  return [
@@ -71,15 +78,15 @@ def add_available_ipaddresses(prefix: netaddr.IPNetwork, ipaddress_list: Iterabl
71
78
  ipaddress_list.sort(key=lambda ip: ip.host)
72
79
 
73
80
  # Account for any available IPs before the first real IP
74
- if ipaddress_list[0].address.ip > first_ip_in_prefix:
75
- skipped_count = int(ipaddress_list[0].address.ip - first_ip_in_prefix)
81
+ if ipaddress_list[0].address.ip.value > first_ip_in_prefix.value:
82
+ skipped_count = ipaddress_list[0].address.ip.value - first_ip_in_prefix.value
76
83
  first_skipped = f"{first_ip_in_prefix}/{prefix.prefixlen}"
77
84
  output.append((skipped_count, first_skipped))
78
85
 
79
86
  # Iterate through existing IPs and annotate free ranges
80
87
  for ip in ipaddress_list:
81
88
  if prev_ip:
82
- diff = int(ip.address.ip - prev_ip.address.ip)
89
+ diff = ip.address.ip.value - prev_ip.address.ip.value
83
90
  if diff > 1:
84
91
  first_skipped = f"{prev_ip.address.ip + 1}/{prefix.prefixlen}"
85
92
  output.append((diff - 1, first_skipped))
@@ -88,7 +95,7 @@ def add_available_ipaddresses(prefix: netaddr.IPNetwork, ipaddress_list: Iterabl
88
95
 
89
96
  # Include any remaining available IPs
90
97
  if prev_ip.address.ip < last_ip_in_prefix:
91
- skipped_count = int(last_ip_in_prefix - prev_ip.address.ip)
98
+ skipped_count = last_ip_in_prefix.value - prev_ip.address.ip.value
92
99
  first_skipped = f"{prev_ip.address.ip + 1}/{prefix.prefixlen}"
93
100
  output.append((skipped_count, first_skipped))
94
101