nautobot 2.4.12__py3-none-any.whl → 2.4.14__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.
Files changed (366) hide show
  1. nautobot/core/graphql/generators.py +8 -0
  2. nautobot/core/graphql/schema.py +30 -30
  3. nautobot/core/management/commands/migrate.py +90 -1
  4. nautobot/core/settings.yaml +3 -3
  5. nautobot/core/tables.py +4 -4
  6. nautobot/core/templates/inc/footer.html +4 -4
  7. nautobot/core/testing/api.py +7 -0
  8. nautobot/core/views/utils.py +1 -1
  9. nautobot/dcim/choices.py +2 -0
  10. nautobot/dcim/constants.py +0 -16
  11. nautobot/dcim/factory.py +1 -1
  12. nautobot/dcim/migrations/0071_alter_consoleport_options_and_more.py +42 -0
  13. nautobot/dcim/models/device_components.py +10 -2
  14. nautobot/dcim/models/devices.py +18 -0
  15. nautobot/dcim/templates/dcim/device.html +1 -34
  16. nautobot/dcim/templates/dcim/rack.html +2 -318
  17. nautobot/dcim/templates/dcim/rack_edit.html +2 -47
  18. nautobot/dcim/templates/dcim/rack_elevation_list.html +4 -1
  19. nautobot/dcim/templates/dcim/rack_retrieve.html +318 -0
  20. nautobot/dcim/templates/dcim/rack_update.html +47 -0
  21. nautobot/dcim/tests/test_models.py +1 -0
  22. nautobot/dcim/urls.py +3 -28
  23. nautobot/dcim/utils.py +4 -30
  24. nautobot/dcim/views.py +73 -71
  25. nautobot/extras/choices.py +12 -4
  26. nautobot/extras/filters/mixins.py +8 -6
  27. nautobot/extras/forms/forms.py +9 -0
  28. nautobot/extras/forms/mixins.py +4 -2
  29. nautobot/extras/migrations/0062_collect_roles_from_related_apps_roles.py +30 -7
  30. nautobot/extras/migrations/0124_add_joblogentry_index.py +16 -0
  31. nautobot/extras/models/customfields.py +52 -3
  32. nautobot/extras/models/jobs.py +6 -0
  33. nautobot/extras/models/relationships.py +55 -6
  34. nautobot/extras/templates/extras/graphqlquery.html +2 -97
  35. nautobot/extras/templates/extras/graphqlquery_list.html +1 -0
  36. nautobot/extras/templates/extras/graphqlquery_retrieve.html +97 -0
  37. nautobot/extras/templates/extras/secretsgroup.html +2 -29
  38. nautobot/extras/templates/extras/secretsgroup_edit.html +2 -82
  39. nautobot/extras/templates/extras/secretsgroup_retrieve.html +29 -0
  40. nautobot/extras/templates/extras/secretsgroup_update.html +82 -0
  41. nautobot/extras/tests/test_customfields.py +115 -7
  42. nautobot/extras/tests/test_relationships.py +7 -1
  43. nautobot/extras/tests/test_views.py +113 -1
  44. nautobot/extras/urls.py +2 -51
  45. nautobot/extras/utils.py +4 -1
  46. nautobot/extras/views.py +42 -135
  47. nautobot/ipam/api/views.py +69 -6
  48. nautobot/ipam/migrations/0052_alter_ipaddress_index_together_and_more.py +28 -0
  49. nautobot/ipam/models.py +13 -1
  50. nautobot/ipam/tests/test_api.py +351 -3
  51. nautobot/ipam/utils/testing.py +76 -29
  52. nautobot/project-static/docs/404.html +11 -34
  53. nautobot/project-static/docs/apps/index.html +11 -34
  54. nautobot/project-static/docs/apps/nautobot-apps.html +11 -34
  55. nautobot/project-static/docs/assets/javascripts/{bundle.56ea9cef.min.js → bundle.50899def.min.js} +2 -2
  56. nautobot/project-static/docs/assets/javascripts/{bundle.56ea9cef.min.js.map → bundle.50899def.min.js.map} +2 -2
  57. nautobot/project-static/docs/assets/stylesheets/{main.342714a4.min.css → main.7e37652d.min.css} +1 -1
  58. nautobot/project-static/docs/assets/stylesheets/{main.342714a4.min.css.map → main.7e37652d.min.css.map} +1 -1
  59. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +11 -34
  60. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +11 -34
  61. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +11 -34
  62. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +11 -34
  63. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +11 -34
  64. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +11 -34
  65. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +11 -34
  66. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +11 -34
  67. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +11 -34
  68. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +11 -34
  69. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +11 -34
  70. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +11 -34
  71. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +11 -34
  72. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +11 -34
  73. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +11 -34
  74. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +11 -34
  75. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +11 -34
  76. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +11 -34
  77. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +11 -34
  78. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +11 -34
  79. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +11 -34
  80. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +11 -34
  81. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +11 -34
  82. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +13 -34
  83. nautobot/project-static/docs/development/apps/api/configuration-view.html +11 -34
  84. nautobot/project-static/docs/development/apps/api/database-backend-config.html +11 -34
  85. nautobot/project-static/docs/development/apps/api/models/django-admin.html +11 -34
  86. nautobot/project-static/docs/development/apps/api/models/global-search.html +11 -34
  87. nautobot/project-static/docs/development/apps/api/models/graphql.html +11 -34
  88. nautobot/project-static/docs/development/apps/api/models/index.html +11 -34
  89. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +11 -34
  90. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +11 -34
  91. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +11 -34
  92. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +11 -34
  93. nautobot/project-static/docs/development/apps/api/platform-features/index.html +11 -34
  94. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +11 -34
  95. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +11 -34
  96. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +11 -34
  97. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +11 -34
  98. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +11 -34
  99. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +11 -34
  100. nautobot/project-static/docs/development/apps/api/prometheus.html +11 -34
  101. nautobot/project-static/docs/development/apps/api/setup.html +11 -34
  102. nautobot/project-static/docs/development/apps/api/testing.html +11 -34
  103. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +11 -34
  104. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +11 -34
  105. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +11 -34
  106. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +11 -34
  107. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +11 -34
  108. nautobot/project-static/docs/development/apps/api/views/base-template.html +11 -34
  109. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +11 -34
  110. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +11 -34
  111. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +11 -34
  112. nautobot/project-static/docs/development/apps/api/views/index.html +11 -34
  113. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +11 -34
  114. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +11 -34
  115. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +11 -34
  116. nautobot/project-static/docs/development/apps/api/views/notes.html +11 -34
  117. nautobot/project-static/docs/development/apps/api/views/rest-api.html +11 -34
  118. nautobot/project-static/docs/development/apps/api/views/urls.html +11 -34
  119. nautobot/project-static/docs/development/apps/index.html +11 -34
  120. nautobot/project-static/docs/development/apps/migration/code-updates.html +11 -34
  121. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +11 -34
  122. nautobot/project-static/docs/development/apps/migration/from-v1.html +11 -34
  123. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +11 -34
  124. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +11 -34
  125. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +11 -34
  126. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +11 -34
  127. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +11 -34
  128. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +11 -34
  129. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +11 -34
  130. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +11 -34
  131. nautobot/project-static/docs/development/apps/porting-from-netbox.html +11 -34
  132. nautobot/project-static/docs/development/core/application-registry.html +139 -133
  133. nautobot/project-static/docs/development/core/best-practices.html +11 -34
  134. nautobot/project-static/docs/development/core/bootstrap-ui.html +11 -34
  135. nautobot/project-static/docs/development/core/caching.html +11 -34
  136. nautobot/project-static/docs/development/core/controllers.html +11 -34
  137. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +11 -34
  138. nautobot/project-static/docs/development/core/generic-views.html +11 -34
  139. nautobot/project-static/docs/development/core/getting-started.html +11 -34
  140. nautobot/project-static/docs/development/core/homepage.html +11 -34
  141. nautobot/project-static/docs/development/core/index.html +11 -34
  142. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +11 -34
  143. nautobot/project-static/docs/development/core/model-checklist.html +11 -34
  144. nautobot/project-static/docs/development/core/model-features.html +11 -34
  145. nautobot/project-static/docs/development/core/natural-keys.html +11 -34
  146. nautobot/project-static/docs/development/core/navigation-menu.html +11 -34
  147. nautobot/project-static/docs/development/core/release-checklist.html +11 -34
  148. nautobot/project-static/docs/development/core/role-internals.html +11 -34
  149. nautobot/project-static/docs/development/core/settings.html +11 -34
  150. nautobot/project-static/docs/development/core/style-guide.html +11 -34
  151. nautobot/project-static/docs/development/core/templates.html +11 -34
  152. nautobot/project-static/docs/development/core/testing.html +11 -34
  153. nautobot/project-static/docs/development/core/ui-component-framework.html +11 -34
  154. nautobot/project-static/docs/development/core/user-preferences.html +11 -34
  155. nautobot/project-static/docs/development/index.html +11 -34
  156. nautobot/project-static/docs/development/jobs/getting-started.html +11 -34
  157. nautobot/project-static/docs/development/jobs/index.html +11 -34
  158. nautobot/project-static/docs/development/jobs/installation.html +11 -34
  159. nautobot/project-static/docs/development/jobs/job-extensions.html +11 -34
  160. nautobot/project-static/docs/development/jobs/job-logging.html +11 -34
  161. nautobot/project-static/docs/development/jobs/job-patterns.html +11 -34
  162. nautobot/project-static/docs/development/jobs/job-structure.html +11 -34
  163. nautobot/project-static/docs/development/jobs/migration/from-v1.html +11 -34
  164. nautobot/project-static/docs/development/jobs/testing.html +11 -34
  165. nautobot/project-static/docs/index.html +11 -34
  166. nautobot/project-static/docs/overview/application_stack.html +11 -34
  167. nautobot/project-static/docs/overview/design_philosophy.html +11 -34
  168. nautobot/project-static/docs/release-notes/index.html +11 -34
  169. nautobot/project-static/docs/release-notes/version-1.0.html +11 -34
  170. nautobot/project-static/docs/release-notes/version-1.1.html +11 -34
  171. nautobot/project-static/docs/release-notes/version-1.2.html +11 -34
  172. nautobot/project-static/docs/release-notes/version-1.3.html +11 -34
  173. nautobot/project-static/docs/release-notes/version-1.4.html +11 -34
  174. nautobot/project-static/docs/release-notes/version-1.5.html +11 -34
  175. nautobot/project-static/docs/release-notes/version-1.6.html +11 -34
  176. nautobot/project-static/docs/release-notes/version-2.0.html +11 -34
  177. nautobot/project-static/docs/release-notes/version-2.1.html +11 -34
  178. nautobot/project-static/docs/release-notes/version-2.2.html +11 -34
  179. nautobot/project-static/docs/release-notes/version-2.3.html +11 -34
  180. nautobot/project-static/docs/release-notes/version-2.4.html +271 -34
  181. nautobot/project-static/docs/requirements.txt +1 -1
  182. nautobot/project-static/docs/search/search_index.json +1 -1
  183. nautobot/project-static/docs/sitemap.xml +299 -299
  184. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  185. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +11 -34
  186. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +11 -34
  187. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +11 -34
  188. nautobot/project-static/docs/user-guide/administration/configuration/index.html +11 -34
  189. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +11 -34
  190. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +14 -37
  191. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +11 -34
  192. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +11 -34
  193. nautobot/project-static/docs/user-guide/administration/guides/docker.html +11 -34
  194. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +11 -34
  195. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +11 -34
  196. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +11 -34
  197. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +11 -34
  198. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +11 -34
  199. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +11 -34
  200. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +11 -34
  201. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +11 -34
  202. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +11 -34
  203. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +11 -34
  204. nautobot/project-static/docs/user-guide/administration/installation/index.html +11 -34
  205. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +11 -34
  206. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +11 -34
  207. nautobot/project-static/docs/user-guide/administration/installation/services.html +11 -34
  208. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +11 -34
  209. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +11 -34
  210. nautobot/project-static/docs/user-guide/administration/security/index.html +11 -34
  211. nautobot/project-static/docs/user-guide/administration/security/notices.html +11 -34
  212. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +19 -39
  213. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +11 -34
  214. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +11 -34
  215. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +11 -34
  216. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +11 -34
  217. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +11 -34
  218. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +11 -34
  219. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +11 -34
  220. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +11 -34
  221. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +11 -34
  222. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +11 -34
  223. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +11 -34
  224. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +11 -34
  225. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +11 -34
  226. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +11 -34
  227. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +11 -34
  228. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +11 -34
  229. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +11 -34
  230. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +11 -34
  231. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +11 -34
  232. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +11 -34
  233. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +11 -34
  234. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +11 -34
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +11 -34
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +11 -34
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +11 -34
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +11 -34
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +11 -34
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +11 -34
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +11 -34
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +11 -34
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +11 -34
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +11 -34
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +11 -34
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +11 -34
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +11 -34
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +11 -34
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +11 -34
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +14 -37
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +11 -34
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +11 -34
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +19 -52
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +11 -34
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +11 -34
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +11 -34
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +11 -34
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +14 -37
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +11 -34
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +11 -34
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +11 -34
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +11 -34
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +11 -34
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +11 -34
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +11 -34
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +11 -34
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +11 -34
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +11 -34
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +11 -34
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +11 -34
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +11 -34
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +11 -34
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +11 -34
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +11 -34
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +11 -34
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +11 -34
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +11 -34
  278. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +11 -34
  279. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +11 -34
  280. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +11 -34
  281. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +11 -34
  282. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +11 -34
  283. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +11 -34
  284. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +11 -34
  285. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +11 -34
  286. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +11 -34
  287. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +11 -34
  288. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +11 -34
  289. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +11 -34
  290. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +11 -34
  291. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +11 -34
  292. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +11 -34
  293. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +11 -34
  294. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +11 -34
  295. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +11 -34
  296. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +11 -34
  297. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +11 -34
  298. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +11 -34
  299. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +11 -34
  300. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +11 -34
  301. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +11 -34
  302. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +11 -34
  303. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -34
  304. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +11 -34
  305. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +11 -34
  306. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +11 -34
  307. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +11 -34
  308. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +11 -34
  309. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +11 -34
  310. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +11 -34
  311. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +11 -34
  312. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +11 -34
  313. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +11 -34
  314. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +11 -34
  315. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +11 -34
  316. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +11 -34
  317. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +11 -34
  318. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +11 -34
  319. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +11 -34
  320. nautobot/project-static/docs/user-guide/index.html +11 -34
  321. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +11 -34
  322. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +11 -34
  323. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +11 -34
  324. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +11 -34
  325. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +11 -34
  326. nautobot/project-static/docs/user-guide/platform-functionality/events.html +11 -34
  327. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +11 -34
  328. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +11 -34
  329. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +11 -34
  330. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +11 -34
  331. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +11 -34
  332. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +11 -34
  333. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +11 -34
  334. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +11 -34
  335. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +11 -34
  336. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +11 -34
  337. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +11 -34
  338. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +11 -34
  339. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +11 -34
  340. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +11 -34
  341. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +11 -34
  342. nautobot/project-static/docs/user-guide/platform-functionality/note.html +11 -34
  343. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +11 -34
  344. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +11 -34
  345. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +11 -34
  346. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +11 -34
  347. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +11 -34
  348. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +11 -34
  349. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +11 -34
  350. nautobot/project-static/docs/user-guide/platform-functionality/role.html +11 -34
  351. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +11 -34
  352. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +11 -34
  353. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +11 -34
  354. nautobot/project-static/docs/user-guide/platform-functionality/status.html +11 -34
  355. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +11 -34
  356. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +11 -34
  357. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +11 -34
  358. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +11 -34
  359. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +11 -34
  360. nautobot/tenancy/api/views.py +2 -1
  361. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/METADATA +5 -5
  362. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/RECORD +366 -357
  363. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/LICENSE.txt +0 -0
  364. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/NOTICE +0 -0
  365. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/WHEEL +0 -0
  366. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/entry_points.txt +0 -0
@@ -316,9 +316,7 @@ class VRFPrefixAssignmentTest(APIViewTestCases.APIViewTestCase):
316
316
  response, "The fields vrf, prefix must make a unique set.", status_code=status.HTTP_400_BAD_REQUEST
317
317
  )
318
318
  response = self.client.post(self._get_list_url(), wrong_namespace_create_data, format="json", **self.header)
319
- self.assertContains(
320
- response, "Prefix must be in same namespace as VRF", status_code=status.HTTP_400_BAD_REQUEST
321
- )
319
+ self.assertContains(response, "must be in same namespace as", status_code=status.HTTP_400_BAD_REQUEST)
322
320
  response = self.client.post(self._get_list_url(), missing_field_create_data, format="json", **self.header)
323
321
  self.assertContains(response, "This field may not be null.", status_code=status.HTTP_400_BAD_REQUEST)
324
322
 
@@ -770,6 +768,299 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
770
768
  response = self.client.get(url, **self.header)
771
769
  self.assertEqual(len(response.data), 6) # 8 - 2 because prefix.type = network
772
770
 
771
+ def test_ipv4_prefix_list_available_ips_with_limit(self):
772
+ """
773
+ Test retrieval with a limit of all available IP addresses within a parent IPv4 prefix.
774
+ """
775
+ prefix = Prefix.objects.create(
776
+ prefix="192.0.3.0/29",
777
+ type=choices.PrefixTypeChoices.TYPE_POOL,
778
+ namespace=self.namespace,
779
+ status=self.status,
780
+ )
781
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
782
+ limit = 2
783
+ url = self.add_query_params_to_url(
784
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
785
+ query_dict={"limit": limit},
786
+ )
787
+ response = self.client.get(url, **self.header)
788
+ self.assertEqual(len(response.data), limit)
789
+
790
+ def test_ipv6_prefix_list_available_ips_with_limit(self):
791
+ """
792
+ Test retrieval with a limit of all available IP addresses within a parent IPv6 prefix.
793
+ """
794
+ prefix = Prefix.objects.create(
795
+ prefix="fdff:abcd:ffff:fc00::/54",
796
+ type=choices.PrefixTypeChoices.TYPE_POOL,
797
+ namespace=self.namespace,
798
+ status=self.status,
799
+ )
800
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
801
+ limit = 100
802
+ url = self.add_query_params_to_url(
803
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
804
+ query_dict={"limit": limit},
805
+ )
806
+ response = self.client.get(url, **self.header)
807
+ self.assertEqual(len(response.data), limit)
808
+
809
+ def test_range_parameters_validation(self):
810
+ """
811
+ Test detection of incorrect range_start or range_end parameters.
812
+ """
813
+ prefix = Prefix.objects.create(
814
+ prefix="fdff:abcd:ffff:fc00::/54",
815
+ type=choices.PrefixTypeChoices.TYPE_POOL,
816
+ namespace=self.namespace,
817
+ status=self.status,
818
+ )
819
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
820
+
821
+ range_start = "fdffZabcdZfffZfc01" # purposefully incorrect parameter
822
+ url = self.add_query_params_to_url(
823
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
824
+ query_dict={"range_start": range_start},
825
+ )
826
+ response = self.client.get(url, **self.header)
827
+ self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
828
+ self.assertIn("detail", response.data)
829
+
830
+ range_end = "fdffZabcdZfffZfcff" # purposefully incorrect parameter
831
+ url = self.add_query_params_to_url(
832
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
833
+ query_dict={"range_end": range_end},
834
+ )
835
+ response = self.client.get(url, **self.header)
836
+ self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
837
+ self.assertIn("detail", response.data)
838
+
839
+ def test_list_available_ips_with_range_start(self):
840
+ """
841
+ Test retrieval with range_start parameter of all available IP addresses within a parent prefix.
842
+ """
843
+ prefix = Prefix.objects.create(
844
+ prefix="192.0.3.0/29",
845
+ type=choices.PrefixTypeChoices.TYPE_POOL,
846
+ namespace=self.namespace,
847
+ status=self.status,
848
+ )
849
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
850
+ range_start = "192.0.3.2"
851
+ url = self.add_query_params_to_url(
852
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
853
+ query_dict={"range_start": range_start},
854
+ )
855
+ response = self.client.get(url, **self.header)
856
+ self.assertEqual(len(response.data), 6) # because prefix type = pool
857
+ self.assertIn("address", response.data[0])
858
+ self.assertIn("192.0.3.2/29", response.data[0]["address"])
859
+
860
+ def test_list_available_ips_with_range_start_ipv6(self):
861
+ """
862
+ Test retrieval with range_start parameter of all available IPv6 addresses within a parent prefix.
863
+ """
864
+ prefix = Prefix.objects.create(
865
+ prefix="fdff:abcd:ffff:fc00::/54",
866
+ type=choices.PrefixTypeChoices.TYPE_POOL,
867
+ namespace=self.namespace,
868
+ status=self.status,
869
+ )
870
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
871
+ range_start = "fdff:abcd:ffff:ffff:ffff:ffff:ffff:fff0"
872
+ url = self.add_query_params_to_url(
873
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
874
+ query_dict={"range_start": range_start},
875
+ )
876
+ response = self.client.get(url, **self.header)
877
+ self.assertEqual(len(response.data), 16)
878
+ self.assertIn("address", response.data[0])
879
+ self.assertIn("fdff:abcd:ffff:ffff:ffff:ffff:ffff:fff0/54", response.data[0]["address"])
880
+
881
+ def test_list_available_ips_with_range_end(self):
882
+ """
883
+ Test retrieval with range_end parameter of all available IP addresses within a parent prefix.
884
+ """
885
+ prefix = Prefix.objects.create(
886
+ prefix="192.0.3.0/29",
887
+ type=choices.PrefixTypeChoices.TYPE_POOL,
888
+ namespace=self.namespace,
889
+ status=self.status,
890
+ )
891
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
892
+ range_end = "192.0.3.5"
893
+ url = self.add_query_params_to_url(
894
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
895
+ query_dict={"range_end": range_end},
896
+ )
897
+ response = self.client.get(url, **self.header)
898
+ self.assertEqual(len(response.data), 6) # because prefix type = pool
899
+ self.assertIn("address", response.data[-1])
900
+ self.assertIn("192.0.3.5/29", response.data[-1]["address"])
901
+
902
+ def test_list_available_ips_with_range_end_ipv6(self):
903
+ """
904
+ Test retrieval with range_end parameter of all available IPv6 addresses within a parent prefix.
905
+ """
906
+ prefix = Prefix.objects.create(
907
+ prefix="fdff:abcd:ffff:fc00::/54",
908
+ type=choices.PrefixTypeChoices.TYPE_POOL,
909
+ namespace=self.namespace,
910
+ status=self.status,
911
+ )
912
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
913
+ range_end = "fdff:abcd:ffff:fc00::000f"
914
+ url = self.add_query_params_to_url(
915
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
916
+ query_dict={"range_end": range_end},
917
+ )
918
+ response = self.client.get(url, **self.header)
919
+ self.assertEqual(len(response.data), 16) # because prefix type = pool
920
+ self.assertIn("address", response.data[-1])
921
+ self.assertIn("fdff:abcd:ffff:fc00::f/54", response.data[-1]["address"])
922
+
923
+ def test_list_available_ips_with_range_start_and_end(self):
924
+ """
925
+ Test retrieval with range_end parameter of all available IP addresses within a parent prefix.
926
+ """
927
+ prefix = Prefix.objects.create(
928
+ prefix="192.0.3.0/29",
929
+ type=choices.PrefixTypeChoices.TYPE_POOL,
930
+ namespace=self.namespace,
931
+ status=self.status,
932
+ )
933
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
934
+ range_start = "192.0.3.2"
935
+ range_end = "192.0.3.5"
936
+ url = self.add_query_params_to_url(
937
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
938
+ query_dict={"range_start": range_start, "range_end": range_end},
939
+ )
940
+ response = self.client.get(url, **self.header)
941
+ self.assertEqual(len(response.data), 4)
942
+ self.assertIn("address", response.data[0])
943
+ self.assertIn("address", response.data[-1])
944
+ self.assertIn("192.0.3.2/29", response.data[0]["address"])
945
+ self.assertIn("192.0.3.5/29", response.data[-1]["address"])
946
+
947
+ def test_list_available_ips_with_range_start_and_end_ipv6(self):
948
+ """
949
+ Test retrieval with range_end parameter of all available IPv6 addresses within a parent prefix.
950
+ """
951
+ prefix = Prefix.objects.create(
952
+ prefix="fdff:abcd:ffff:fc00::/54",
953
+ type=choices.PrefixTypeChoices.TYPE_POOL,
954
+ namespace=self.namespace,
955
+ status=self.status,
956
+ )
957
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
958
+ range_start = "fdff:abcd:ffff:fcff::"
959
+ range_end = "fdff:abcd:ffff:fcff::f"
960
+ url = self.add_query_params_to_url(
961
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
962
+ query_dict={"range_start": range_start, "range_end": range_end},
963
+ )
964
+ response = self.client.get(url, **self.header)
965
+ self.assertEqual(len(response.data), 16)
966
+ self.assertIn("address", response.data[0])
967
+ self.assertIn("address", response.data[-1])
968
+ self.assertIn("fdff:abcd:ffff:fcff::/54", response.data[0]["address"])
969
+ self.assertIn("fdff:abcd:ffff:fcff::f/54", response.data[-1]["address"])
970
+
971
+ def test_list_available_ips_with_range_start_and_limit(self):
972
+ """
973
+ Test retrieval with range_start and limit of all available IP addresses within a parent prefix.
974
+ """
975
+ prefix = Prefix.objects.create(
976
+ prefix="192.0.3.0/29",
977
+ type=choices.PrefixTypeChoices.TYPE_POOL,
978
+ namespace=self.namespace,
979
+ status=self.status,
980
+ )
981
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
982
+ range_start = "192.0.3.2"
983
+ limit = 2
984
+ url = self.add_query_params_to_url(
985
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
986
+ query_dict={"range_start": range_start, "limit": limit},
987
+ )
988
+ response = self.client.get(url, **self.header)
989
+ self.assertEqual(len(response.data), limit)
990
+ self.assertIn("address", response.data[0])
991
+ self.assertIn("192.0.3.2/29", response.data[0]["address"])
992
+
993
+ def test_list_available_ips_with_range_start_and_limit_ipv6(self):
994
+ """
995
+ Test retrieval with range_start and limit of all available IPv6 addresses within a parent prefix.
996
+ """
997
+ prefix = Prefix.objects.create(
998
+ prefix="fdff:abcd:ffff:fc00::/54",
999
+ type=choices.PrefixTypeChoices.TYPE_POOL,
1000
+ namespace=self.namespace,
1001
+ status=self.status,
1002
+ )
1003
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
1004
+ range_start = "fdff:abcd:ffff:ffff:ffff:ffff:ffff:fff0"
1005
+ limit = 2
1006
+ url = self.add_query_params_to_url(
1007
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
1008
+ query_dict={"range_start": range_start, "limit": limit},
1009
+ )
1010
+ response = self.client.get(url, **self.header)
1011
+ self.assertEqual(len(response.data), limit)
1012
+ self.assertIn("fdff:abcd:ffff:ffff:ffff:ffff:ffff:fff0/54", response.data[0]["address"])
1013
+
1014
+ def test_list_available_ips_with_range_start_range_end_and_limit(self):
1015
+ """
1016
+ Test retrieval with range_start, range_end and limit of all available IP addresses within a parent prefix.
1017
+ """
1018
+ prefix = Prefix.objects.create(
1019
+ prefix="192.0.3.0/29",
1020
+ type=choices.PrefixTypeChoices.TYPE_POOL,
1021
+ namespace=self.namespace,
1022
+ status=self.status,
1023
+ )
1024
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
1025
+ range_start = "192.0.3.2"
1026
+ range_end = "192.0.3.5"
1027
+ limit = 2
1028
+ url = self.add_query_params_to_url(
1029
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
1030
+ query_dict={"range_start": range_start, "range_end": range_end, "limit": limit},
1031
+ )
1032
+ response = self.client.get(url, **self.header)
1033
+ self.assertEqual(len(response.data), limit)
1034
+ self.assertIn("address", response.data[0])
1035
+ self.assertIn("address", response.data[-1])
1036
+ self.assertIn("192.0.3.2/29", response.data[0]["address"])
1037
+ self.assertIn("192.0.3.3/29", response.data[-1]["address"]) # .3 is the next one
1038
+
1039
+ def test_list_available_ips_with_range_start_range_end_and_limit_ipv6(self):
1040
+ """
1041
+ Test retrieval with range_start, range_end and limit of all available IPv6 addresses within a parent prefix.
1042
+ """
1043
+ prefix = Prefix.objects.create(
1044
+ prefix="fdff:abcd:ffff:fc00::/54",
1045
+ type=choices.PrefixTypeChoices.TYPE_POOL,
1046
+ namespace=self.namespace,
1047
+ status=self.status,
1048
+ )
1049
+ self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
1050
+ range_start = "fdff:abcd:ffff:fcff::"
1051
+ range_end = "fdff:abcd:ffff:fcff::f"
1052
+ limit = 2
1053
+ url = self.add_query_params_to_url(
1054
+ url=reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
1055
+ query_dict={"range_start": range_start, "range_end": range_end, "limit": limit},
1056
+ )
1057
+ response = self.client.get(url, **self.header)
1058
+ self.assertEqual(len(response.data), limit)
1059
+ self.assertIn("address", response.data[0])
1060
+ self.assertIn("address", response.data[-1])
1061
+ self.assertIn("fdff:abcd:ffff:fcff::/54", response.data[0]["address"])
1062
+ self.assertIn("fdff:abcd:ffff:fcff::1/54", response.data[-1]["address"])
1063
+
773
1064
  def test_list_available_ips_calculate_child_ips(self):
774
1065
  """
775
1066
  Test retrieval of all available IP addresses when child IP's exists.
@@ -873,6 +1164,36 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
873
1164
  self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
874
1165
  self.assertIn("detail", response.data)
875
1166
 
1167
+ def test_create_single_available_ip_with_range_start(self):
1168
+ prefix = Prefix.objects.create(
1169
+ prefix="192.0.2.0/29",
1170
+ namespace=self.namespace,
1171
+ type=choices.PrefixTypeChoices.TYPE_NETWORK,
1172
+ status=self.status,
1173
+ )
1174
+ self.add_permissions("ipam.view_prefix", "ipam.view_namespace", "ipam.add_ipaddress", "extras.view_status")
1175
+
1176
+ range_start = "192.0.2.3"
1177
+ url = self.add_query_params_to_url(
1178
+ reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
1179
+ {"range_start": range_start},
1180
+ )
1181
+ # Create available IPs with range start
1182
+ for i in range(1, 5):
1183
+ data = {
1184
+ "description": f"Test IP {i}",
1185
+ "status": self.status.pk,
1186
+ }
1187
+ response = self.client.post(url, data, format="json", **self.header)
1188
+ self.assertHttpStatus(response, status.HTTP_201_CREATED)
1189
+ self.assertEqual(str(response.data["parent"]["url"]), self.absolute_api_url(prefix))
1190
+ self.assertEqual(response.data["description"], data["description"])
1191
+
1192
+ # Next creation request with range start should be denied
1193
+ response = self.client.post(url, {"status": self.status.pk}, format="json", **self.header)
1194
+ self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
1195
+ self.assertIn("detail", response.data)
1196
+
876
1197
  def test_create_multiple_available_ips(self):
877
1198
  """
878
1199
  Test the creation of available IP addresses within a parent prefix.
@@ -913,6 +1234,33 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
913
1234
  self.assertIn("ipcf", response.data[0]["custom_fields"])
914
1235
  self.assertEqual("1", response.data[0]["custom_fields"]["ipcf"])
915
1236
 
1237
+ def test_create_multiple_available_ips_with_range_start(self):
1238
+ prefix = Prefix.objects.create(
1239
+ prefix="192.0.2.0/29",
1240
+ type=choices.PrefixTypeChoices.TYPE_NETWORK,
1241
+ namespace=self.namespace,
1242
+ status=self.status,
1243
+ )
1244
+ self.add_permissions("ipam.view_prefix", "ipam.add_ipaddress", "ipam.view_namespace", "extras.view_status")
1245
+
1246
+ range_start = "192.0.2.3"
1247
+ url = self.add_query_params_to_url(
1248
+ reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk}),
1249
+ {"range_start": range_start},
1250
+ )
1251
+ # create all 5 available IPs in a single request
1252
+ data = [
1253
+ {"description": f"Test IP {i}", "status": self.status.pk, "custom_fields": {"ipcf": str(i)}}
1254
+ for i in range(1, 5)
1255
+ ]
1256
+ response = self.client.post(url, data, format="json", **self.header)
1257
+ self.assertHttpStatus(response, status.HTTP_201_CREATED)
1258
+ self.assertEqual(len(response.data), 4)
1259
+ # next attempt with only one IP requested should fail (prefix space exhausted)
1260
+ data = data[0]
1261
+ response = self.client.post(url, data, format="json", **self.header)
1262
+ self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
1263
+
916
1264
  def test_create_available_ips_with_permissions_constraint(self):
917
1265
  # Prepare prefix and permissions
918
1266
  prefix = Prefix.objects.create(
@@ -5,6 +5,8 @@ import random
5
5
  from django.apps import apps
6
6
  from netaddr import IPNetwork
7
7
 
8
+ from nautobot.ipam.models import get_default_namespace
9
+
8
10
  # Calculate the probabilities to use for the maybe_subdivide() function defined below.
9
11
 
10
12
  # Frequency of IPv4 (leaf, network) Prefixes by each given mask length in a "realistic" data set.
@@ -113,40 +115,73 @@ def create_prefixes_and_ips(initial_subnet: str, apps=apps, seed="Nautobot"): #
113
115
  status_active, _ = Status.objects.get_or_create(name="Active", defaults={"slug": "active"})
114
116
 
115
117
  for i in range(1, 11):
116
- Tenant.objects.create(name=f"{initial_subnet} Tenant {i}")
117
- VRF.objects.create(name=f"{initial_subnet} VRF {i}", enforce_unique=False) # TODO should some enforce_unique?
118
+ Tenant.objects.get_or_create(name=f"{initial_subnet} Tenant {i}")
119
+ if hasattr(VRF, "enforce_unique"):
120
+ VRF.objects.get_or_create(
121
+ name=f"{initial_subnet} VRF {i}",
122
+ enforce_unique=False, # TODO should some enforce_unique?
123
+ )
124
+ else:
125
+ VRF.objects.get_or_create(name=f"{initial_subnet} VRF {i}")
118
126
 
119
127
  all_tenants = list(Tenant.objects.all())
120
- all_vrfs = list(VRF.objects.all())
128
+ if hasattr(VRF, "namespace"):
129
+ all_vrfs = list(VRF.objects.filter(namespace=get_default_namespace()))
130
+ else:
131
+ all_vrfs = list(VRF.objects.all())
132
+
133
+ create_prefixes(initial_subnet, all_tenants, all_vrfs, status_active, Prefix)
134
+ create_ips(initial_subnet, all_tenants, all_vrfs, status_active, IPAddress)
121
135
 
136
+
137
+ def create_prefixes(initial_subnet, all_tenants, all_vrfs, status_active, Prefix):
122
138
  print(f"Creating Prefixes to subdivide {initial_subnet}")
123
139
  unique_prefix_count = 0
124
140
  duplicate_prefix_count = 0
125
141
  for subnet in maybe_subdivide(IPNetwork(initial_subnet)):
126
142
  if random.random() < 0.95: # noqa: S311 # suspicious-non-cryptographic-random-usage
127
143
  # 95% chance to create any given Prefix
128
- Prefix.objects.create(
129
- network=str(subnet.network),
130
- broadcast=str(subnet.broadcast if subnet.broadcast else subnet[-1]),
131
- prefix_length=subnet.prefixlen,
132
- status=status_active,
133
- tenant=maybe_random_instance(all_tenants),
134
- vrf=maybe_random_instance(all_vrfs),
135
- )
136
- unique_prefix_count += 1
137
- while random.random() < 0.1: # noqa: S311 # suspicious-non-cryptographic-random-usage
138
- # 10% repeating chance to create a duplicate(s) of this Prefix
139
- Prefix.objects.create(
144
+ vrf = maybe_random_instance(all_vrfs)
145
+ if hasattr(Prefix, "vrf"):
146
+ Prefix.objects.get_or_create(
140
147
  network=str(subnet.network),
141
148
  broadcast=str(subnet.broadcast if subnet.broadcast else subnet[-1]),
142
149
  prefix_length=subnet.prefixlen,
143
150
  status=status_active,
144
151
  tenant=maybe_random_instance(all_tenants),
145
- vrf=maybe_random_instance(all_vrfs),
152
+ vrf=vrf,
146
153
  )
147
- duplicate_prefix_count += 1
154
+ else:
155
+ prefix, _ = Prefix.objects.get_or_create(
156
+ network=str(subnet.network),
157
+ broadcast=str(subnet.broadcast if subnet.broadcast else subnet[-1]),
158
+ prefix_length=subnet.prefixlen,
159
+ status=status_active,
160
+ tenant=maybe_random_instance(all_tenants),
161
+ )
162
+ if vrf is not None:
163
+ vrf.add_prefix(prefix)
164
+
165
+ unique_prefix_count += 1
166
+ if hasattr(Prefix, "vrf"):
167
+ while random.random() < 0.1: # noqa: S311 # suspicious-non-cryptographic-random-usage
168
+ # 10% repeating chance to create a duplicate(s) of this Prefix
169
+ Prefix.objects.create(
170
+ network=str(subnet.network),
171
+ broadcast=str(subnet.broadcast if subnet.broadcast else subnet[-1]),
172
+ prefix_length=subnet.prefixlen,
173
+ status=status_active,
174
+ tenant=maybe_random_instance(all_tenants),
175
+ vrf=maybe_random_instance(all_vrfs),
176
+ )
177
+ duplicate_prefix_count += 1
178
+ else:
179
+ # TODO: create prefixes in different namespaces?
180
+ pass
148
181
  print(f"Created {unique_prefix_count} unique Prefixes and {duplicate_prefix_count} duplicates")
149
182
 
183
+
184
+ def create_ips(initial_subnet, all_tenants, all_vrfs, status_active, IPAddress):
150
185
  print(f"Creating IPAddresses within {initial_subnet}")
151
186
  unique_ip_count = 0
152
187
  duplicate_ip_count = 0
@@ -154,17 +189,7 @@ def create_prefixes_and_ips(initial_subnet: str, apps=apps, seed="Nautobot"): #
154
189
  if random.random() < 0.05: # noqa: S311 # suspicious-non-cryptographic-random-usage
155
190
  # 5% chance to create any given IP address
156
191
  network = IPNetwork(ip)
157
- IPAddress.objects.create(
158
- host=str(network.ip),
159
- broadcast=str(network.broadcast if network.broadcast else network[-1]),
160
- prefix_length=network.prefixlen,
161
- status=status_active,
162
- tenant=maybe_random_instance(all_tenants),
163
- vrf=maybe_random_instance(all_vrfs),
164
- )
165
- unique_ip_count += 1
166
- while random.random() < 0.1: # noqa: S311 # suspicious-non-cryptographic-random-usage
167
- # 10% repeating chance to create a duplicate(s) of this IP
192
+ if hasattr(IPAddress, "prefix_length"):
168
193
  IPAddress.objects.create(
169
194
  host=str(network.ip),
170
195
  broadcast=str(network.broadcast if network.broadcast else network[-1]),
@@ -173,5 +198,27 @@ def create_prefixes_and_ips(initial_subnet: str, apps=apps, seed="Nautobot"): #
173
198
  tenant=maybe_random_instance(all_tenants),
174
199
  vrf=maybe_random_instance(all_vrfs),
175
200
  )
176
- duplicate_ip_count += 1
201
+ else:
202
+ IPAddress.objects.create(
203
+ host=str(network.ip),
204
+ mask_length=network.prefixlen,
205
+ status=status_active,
206
+ tenant=maybe_random_instance(all_tenants),
207
+ )
208
+ unique_ip_count += 1
209
+ if hasattr(IPAddress, "prefix_length"):
210
+ while random.random() < 0.1: # noqa: S311 # suspicious-non-cryptographic-random-usage
211
+ # 10% repeating chance to create a duplicate(s) of this IP
212
+ IPAddress.objects.create(
213
+ host=str(network.ip),
214
+ broadcast=str(network.broadcast if network.broadcast else network[-1]),
215
+ prefix_length=network.prefixlen,
216
+ status=status_active,
217
+ tenant=maybe_random_instance(all_tenants),
218
+ vrf=maybe_random_instance(all_vrfs),
219
+ )
220
+ duplicate_ip_count += 1
221
+ else:
222
+ # TODO: create duplicate IPs in other namespaces?
223
+ pass
177
224
  print(f"Created {unique_ip_count} unique IPAddresses and {duplicate_ip_count} duplicates")