nautobot 2.4.15__py3-none-any.whl → 2.4.17__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 (374) hide show
  1. nautobot/apps/utils.py +2 -0
  2. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +3 -3
  3. nautobot/cloud/views.py +7 -0
  4. nautobot/core/apps/__init__.py +1 -0
  5. nautobot/core/celery/__init__.py +2 -1
  6. nautobot/core/templates/components/panel/panel.html +1 -1
  7. nautobot/core/templates/inc/paginator.html +3 -3
  8. nautobot/core/templates/inc/table.html +2 -2
  9. nautobot/core/templatetags/helpers.py +80 -6
  10. nautobot/core/testing/mixins.py +1 -1
  11. nautobot/core/testing/views.py +2 -4
  12. nautobot/core/ui/bulk_buttons.py +53 -53
  13. nautobot/core/ui/object_detail.py +9 -4
  14. nautobot/core/utils/data.py +13 -0
  15. nautobot/core/utils/deprecation.py +2 -0
  16. nautobot/dcim/migrations/0073_alter_powerport_power_factor_and_more.py +41 -0
  17. nautobot/dcim/models/device_component_templates.py +4 -2
  18. nautobot/dcim/models/device_components.py +3 -2
  19. nautobot/dcim/templates/dcim/rack_elevation_list.html +4 -4
  20. nautobot/dcim/views.py +9 -0
  21. nautobot/extras/models/customfields.py +45 -9
  22. nautobot/extras/tables.py +12 -0
  23. nautobot/extras/templates/extras/configcontext_retrieve.html +1 -1
  24. nautobot/extras/templates/extras/configcontext_update.html +49 -49
  25. nautobot/extras/templates/extras/configcontextschema_retrieve.html +47 -47
  26. nautobot/extras/templates/extras/configcontextschema_update.html +18 -18
  27. nautobot/extras/templates/extras/inc/job_table.html +1 -1
  28. nautobot/extras/templates/extras/inc/object_contact_header.html +2 -2
  29. nautobot/extras/templates/extras/marketplace.html +1 -1
  30. nautobot/extras/templates/extras/note_retrieve.html +53 -53
  31. nautobot/extras/templates/extras/secretsgroup_retrieve.html +2 -29
  32. nautobot/extras/templates/extras/tag_retrieve.html +1 -1
  33. nautobot/extras/templates/extras/tag_update.html +14 -14
  34. nautobot/extras/templates/extras/team_retrieve.html +1 -1
  35. nautobot/extras/tests/test_models.py +216 -0
  36. nautobot/extras/tests/test_plugins.py +12 -0
  37. nautobot/extras/tests/test_views.py +2 -2
  38. nautobot/extras/views.py +20 -4
  39. nautobot/ipam/apps.py +1 -0
  40. nautobot/ipam/jobs/__init__.py +10 -0
  41. nautobot/ipam/jobs/cleanup.py +296 -0
  42. nautobot/ipam/models.py +301 -178
  43. nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +3 -3
  44. nautobot/ipam/templates/ipam/inc/toggle_available.html +2 -2
  45. nautobot/ipam/templates/ipam/ipaddress_assign.html +1 -1
  46. nautobot/ipam/templates/ipam/prefix_list.html +1 -1
  47. nautobot/ipam/templates/ipam/vlan_retrieve.html +1 -77
  48. nautobot/ipam/tests/test_jobs.py +454 -0
  49. nautobot/ipam/tests/test_models.py +290 -122
  50. nautobot/ipam/tests/test_views.py +40 -164
  51. nautobot/ipam/urls.py +0 -11
  52. nautobot/ipam/utils/testing.py +9 -4
  53. nautobot/ipam/views.py +166 -235
  54. nautobot/project-static/docs/404.html +9 -6
  55. nautobot/project-static/docs/apps/index.html +9 -6
  56. nautobot/project-static/docs/apps/nautobot-apps.html +9 -6
  57. nautobot/project-static/docs/assets/javascripts/bundle.92b07e13.min.js +16 -0
  58. nautobot/project-static/docs/assets/javascripts/{bundle.50899def.min.js.map → bundle.92b07e13.min.js.map} +2 -2
  59. nautobot/project-static/docs/assets/javascripts/workers/{search.d50fe291.min.js → search.973d3a69.min.js} +4 -4
  60. nautobot/project-static/docs/assets/javascripts/workers/{search.d50fe291.min.js.map → search.973d3a69.min.js.map} +1 -1
  61. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +9 -6
  62. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +9 -6
  63. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +9 -6
  64. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +9 -6
  65. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +10 -7
  66. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +9 -6
  67. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +9 -6
  68. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +9 -6
  69. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9 -6
  70. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +9 -6
  71. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +9 -6
  72. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +9 -6
  73. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +9 -6
  74. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +9 -6
  75. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +9 -6
  76. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +11 -8
  77. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +9 -6
  78. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +9 -6
  79. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +11 -8
  80. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9 -6
  81. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +28 -9
  82. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +9 -6
  83. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +69 -7
  84. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +9 -6
  85. nautobot/project-static/docs/development/apps/api/configuration-view.html +13 -10
  86. nautobot/project-static/docs/development/apps/api/database-backend-config.html +11 -8
  87. nautobot/project-static/docs/development/apps/api/models/django-admin.html +13 -10
  88. nautobot/project-static/docs/development/apps/api/models/global-search.html +10 -7
  89. nautobot/project-static/docs/development/apps/api/models/graphql.html +18 -15
  90. nautobot/project-static/docs/development/apps/api/models/index.html +14 -11
  91. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +11 -8
  92. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +15 -12
  93. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +9 -6
  94. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +15 -12
  95. nautobot/project-static/docs/development/apps/api/platform-features/index.html +9 -6
  96. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +11 -8
  97. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +16 -13
  98. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +12 -10305
  99. nautobot/project-static/docs/development/apps/api/platform-features/prepopulating-data.html +10722 -0
  100. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +15 -12
  101. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +14 -11
  102. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +9 -6
  103. nautobot/project-static/docs/development/apps/api/prometheus.html +15 -12
  104. nautobot/project-static/docs/development/apps/api/setup.html +9 -6
  105. nautobot/project-static/docs/development/apps/api/testing.html +9 -6
  106. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +12 -9
  107. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +9 -6
  108. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +9 -6
  109. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +9 -6
  110. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +20 -17
  111. nautobot/project-static/docs/development/apps/api/views/base-template.html +9 -6
  112. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +15 -12
  113. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +14 -11
  114. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +9 -6
  115. nautobot/project-static/docs/development/apps/api/views/index.html +9 -6
  116. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +10 -7
  117. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +24 -21
  118. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +12 -9
  119. nautobot/project-static/docs/development/apps/api/views/notes.html +10 -7
  120. nautobot/project-static/docs/development/apps/api/views/rest-api.html +19 -16
  121. nautobot/project-static/docs/development/apps/api/views/urls.html +11 -8
  122. nautobot/project-static/docs/development/apps/index.html +9 -6
  123. nautobot/project-static/docs/development/apps/migration/code-updates.html +19 -16
  124. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +9 -6
  125. nautobot/project-static/docs/development/apps/migration/from-v1.html +9 -6
  126. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +22 -19
  127. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +9 -6
  128. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +9 -6
  129. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +9 -6
  130. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9 -6
  131. nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +14 -11
  132. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +27 -24
  133. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +20 -17
  134. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +20 -17
  135. nautobot/project-static/docs/development/apps/porting-from-netbox.html +9 -6
  136. nautobot/project-static/docs/development/core/application-registry.html +23 -20
  137. nautobot/project-static/docs/development/core/best-practices.html +23 -20
  138. nautobot/project-static/docs/development/core/bootstrap-ui.html +9 -6
  139. nautobot/project-static/docs/development/core/caching.html +9 -6
  140. nautobot/project-static/docs/development/core/controllers.html +9 -6
  141. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +10 -7
  142. nautobot/project-static/docs/development/core/generic-views.html +9 -6
  143. nautobot/project-static/docs/development/core/getting-started.html +9 -6
  144. nautobot/project-static/docs/development/core/homepage.html +12 -9
  145. nautobot/project-static/docs/development/core/index.html +9 -6
  146. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9 -6
  147. nautobot/project-static/docs/development/core/model-checklist.html +9 -6
  148. nautobot/project-static/docs/development/core/model-features.html +11 -8
  149. nautobot/project-static/docs/development/core/natural-keys.html +21 -18
  150. nautobot/project-static/docs/development/core/navigation-menu.html +10 -7
  151. nautobot/project-static/docs/development/core/release-checklist.html +9 -6
  152. nautobot/project-static/docs/development/core/role-internals.html +9 -6
  153. nautobot/project-static/docs/development/core/settings.html +9 -6
  154. nautobot/project-static/docs/development/core/style-guide.html +32 -29
  155. nautobot/project-static/docs/development/core/templates.html +9 -6
  156. nautobot/project-static/docs/development/core/testing.html +10 -7
  157. nautobot/project-static/docs/development/core/ui-component-framework.html +36 -33
  158. nautobot/project-static/docs/development/core/user-preferences.html +9 -6
  159. nautobot/project-static/docs/development/index.html +9 -6
  160. nautobot/project-static/docs/development/jobs/getting-started.html +13 -10
  161. nautobot/project-static/docs/development/jobs/index.html +9 -6
  162. nautobot/project-static/docs/development/jobs/installation.html +23 -20
  163. nautobot/project-static/docs/development/jobs/job-extensions.html +25 -22
  164. nautobot/project-static/docs/development/jobs/job-logging.html +12 -9
  165. nautobot/project-static/docs/development/jobs/job-patterns.html +45 -42
  166. nautobot/project-static/docs/development/jobs/job-structure.html +53 -50
  167. nautobot/project-static/docs/development/jobs/migration/from-v1.html +23 -20
  168. nautobot/project-static/docs/development/jobs/testing.html +14 -11
  169. nautobot/project-static/docs/index.html +9 -6
  170. nautobot/project-static/docs/objects.inv +0 -0
  171. nautobot/project-static/docs/overview/application_stack.html +9 -6
  172. nautobot/project-static/docs/overview/design_philosophy.html +9 -6
  173. nautobot/project-static/docs/release-notes/index.html +9 -6
  174. nautobot/project-static/docs/release-notes/version-1.0.html +9 -6
  175. nautobot/project-static/docs/release-notes/version-1.1.html +9 -6
  176. nautobot/project-static/docs/release-notes/version-1.2.html +10 -7
  177. nautobot/project-static/docs/release-notes/version-1.3.html +9 -6
  178. nautobot/project-static/docs/release-notes/version-1.4.html +9 -6
  179. nautobot/project-static/docs/release-notes/version-1.5.html +13 -10
  180. nautobot/project-static/docs/release-notes/version-1.6.html +9 -6
  181. nautobot/project-static/docs/release-notes/version-2.0.html +9 -6
  182. nautobot/project-static/docs/release-notes/version-2.1.html +9 -6
  183. nautobot/project-static/docs/release-notes/version-2.2.html +9 -6
  184. nautobot/project-static/docs/release-notes/version-2.3.html +9 -6
  185. nautobot/project-static/docs/release-notes/version-2.4.html +342 -6
  186. nautobot/project-static/docs/requirements.txt +2 -2
  187. nautobot/project-static/docs/search/search_index.json +1 -1
  188. nautobot/project-static/docs/sitemap.xml +301 -301
  189. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  190. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +15 -12
  191. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +9 -6
  192. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +16 -13
  193. nautobot/project-static/docs/user-guide/administration/configuration/index.html +9 -6
  194. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +9 -6
  195. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +11 -8
  196. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +9 -6
  197. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +9 -6
  198. nautobot/project-static/docs/user-guide/administration/guides/docker.html +9 -6
  199. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +9 -6
  200. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +9 -6
  201. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +9 -6
  202. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +9 -6
  203. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +9 -6
  204. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +16 -13
  205. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +9 -6
  206. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +9 -6
  207. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +9 -6
  208. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +9 -6
  209. nautobot/project-static/docs/user-guide/administration/installation/index.html +9 -6
  210. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +9 -6
  211. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +9 -6
  212. nautobot/project-static/docs/user-guide/administration/installation/services.html +12 -9
  213. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +13 -10
  214. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +10 -7
  215. nautobot/project-static/docs/user-guide/administration/security/index.html +9 -6
  216. nautobot/project-static/docs/user-guide/administration/security/notices.html +9 -6
  217. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +9 -6
  218. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +10 -7
  219. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +9 -6
  220. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +9 -6
  221. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +9 -6
  222. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +9 -6
  223. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +9 -6
  224. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +9 -6
  225. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +9 -6
  226. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +15 -12
  227. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +9 -6
  228. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +9 -6
  229. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +9 -6
  230. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +9 -6
  231. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +9 -6
  232. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +9 -6
  233. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +9 -6
  234. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +9 -6
  235. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +9 -6
  236. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +9 -6
  237. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +9 -6
  238. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +9 -6
  239. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +9 -6
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +9 -6
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +9 -6
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +9 -6
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +9 -6
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +9 -6
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +9 -6
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +9 -6
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +9 -6
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +9 -6
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +9 -6
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +9 -6
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +13 -10
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +9 -6
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +9 -6
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +9 -6
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +9 -6
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +9 -6
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +9 -6
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +9 -6
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +9 -6
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +9 -6
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +9 -6
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +9 -6
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +9 -6
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +9 -6
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +9 -6
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +9 -6
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +9 -6
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +9 -6
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +9 -6
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +9 -6
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +9 -6
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +9 -6
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +9 -6
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +9 -6
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +9 -6
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +9 -6
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +9 -6
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +9 -6
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +9 -6
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +9 -6
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +9 -6
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9 -6
  283. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +9 -6
  284. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +9 -6
  285. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +11 -8
  286. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +11 -8
  287. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +41 -41
  288. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +9 -6
  289. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +197 -54
  290. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +9 -6
  291. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +9 -6
  292. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +9 -6
  293. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +9 -6
  294. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +9 -6
  295. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +9 -6
  296. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +9 -6
  297. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +9 -6
  298. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +9 -6
  299. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +9 -6
  300. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +9 -6
  301. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +9 -6
  302. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +9 -6
  303. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +9 -6
  304. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9 -6
  305. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9 -6
  306. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9 -6
  307. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9 -6
  308. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +9 -6
  309. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +9 -6
  310. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +9 -6
  311. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +9 -6
  312. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +9 -6
  313. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +9 -6
  314. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +9 -6
  315. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +9 -6
  316. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +9 -6
  317. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +9 -6
  318. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +9 -6
  319. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +9 -6
  320. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +13 -10
  321. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +9 -6
  322. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +9 -6
  323. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +9 -6
  324. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9 -6
  325. nautobot/project-static/docs/user-guide/index.html +9 -6
  326. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +9 -6
  327. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +9 -6
  328. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +10 -7
  329. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +9 -6
  330. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +9 -6
  331. nautobot/project-static/docs/user-guide/platform-functionality/events.html +11 -8
  332. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +9 -6
  333. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +9 -6
  334. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +9 -6
  335. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +9 -6
  336. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +9 -6
  337. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +9 -6
  338. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +9 -6
  339. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +9 -6
  340. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +9 -6
  341. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +9 -6
  342. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9 -6
  343. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9 -6
  344. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +9 -6
  345. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +9 -6
  346. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +9 -6
  347. nautobot/project-static/docs/user-guide/platform-functionality/note.html +9 -6
  348. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +12 -9
  349. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +9 -6
  350. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9 -6
  351. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +9 -6
  352. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +9 -6
  353. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +9 -6
  354. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +9 -6
  355. nautobot/project-static/docs/user-guide/platform-functionality/role.html +9 -6
  356. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9 -6
  357. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +11 -8
  358. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +9 -6
  359. nautobot/project-static/docs/user-guide/platform-functionality/status.html +9 -6
  360. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +9 -6
  361. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +9 -6
  362. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +9 -6
  363. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +9 -6
  364. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +9 -6
  365. nautobot/project-static/fonts/UFL.txt +96 -96
  366. nautobot/project-static/js/forms.js +35 -2
  367. nautobot/virtualization/filters.py +7 -0
  368. {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/METADATA +6 -6
  369. {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/RECORD +373 -368
  370. nautobot/project-static/docs/assets/javascripts/bundle.50899def.min.js +0 -16
  371. {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/LICENSE.txt +0 -0
  372. {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/NOTICE +0 -0
  373. {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/WHEEL +0 -0
  374. {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/entry_points.txt +0 -0
@@ -406,17 +406,20 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
406
406
  self.status = self.statuses.first()
407
407
  self.status.content_types.add(ContentType.objects.get_for_model(IPAddress))
408
408
  self.root = Prefix.objects.create(
409
- prefix="101.102.0.0/24", status=self.status, namespace=self.namespace, type=PrefixTypeChoices.TYPE_CONTAINER
409
+ prefix="101.102.0.0/16", status=self.status, namespace=self.namespace, type=PrefixTypeChoices.TYPE_CONTAINER
410
410
  )
411
411
  self.parent = Prefix.objects.create(
412
- prefix="101.102.0.0/25", status=self.status, namespace=self.namespace, type=PrefixTypeChoices.TYPE_CONTAINER
412
+ prefix="101.102.103.0/24",
413
+ status=self.status,
414
+ namespace=self.namespace,
415
+ type=PrefixTypeChoices.TYPE_CONTAINER,
413
416
  )
414
- self.child1 = Prefix.objects.create(prefix="101.102.0.0/26", status=self.status, namespace=self.namespace)
415
- self.child2 = Prefix.objects.create(prefix="101.102.0.64/26", status=self.status, namespace=self.namespace)
417
+ self.child1 = Prefix.objects.create(prefix="101.102.103.0/26", status=self.status, namespace=self.namespace)
418
+ self.child2 = Prefix.objects.create(prefix="101.102.103.104/32", status=self.status, namespace=self.namespace)
416
419
 
417
420
  def test_parent_exists_after_model_clean(self):
418
421
  prefix = Prefix(
419
- prefix="101.102.0.128/26",
422
+ prefix="101.102.1.0/24",
420
423
  status=self.status,
421
424
  namespace=self.namespace,
422
425
  type=PrefixTypeChoices.TYPE_CONTAINER,
@@ -626,18 +629,18 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
626
629
  # siblings()
627
630
  self.assertEqual(list(self.child1.siblings()), [self.child2])
628
631
  self.assertEqual(list(self.child1.siblings(include_self=True)), [self.child1, self.child2])
629
- parent2 = Prefix.objects.create(prefix="101.102.0.128/25", status=self.status, namespace=self.namespace)
632
+ parent2 = Prefix.objects.create(prefix="101.102.128.0/24", status=self.status, namespace=self.namespace)
630
633
  self.assertEqual(list(self.parent.siblings()), [parent2])
631
634
  self.assertEqual(list(self.parent.siblings(include_self=True)), [self.parent, parent2])
632
635
 
633
- def test_reparenting(self):
636
+ def test_reparenting_on_create_and_delete(self):
634
637
  """Test that reparenting algorithm works in its most basic form."""
635
638
  # tree hierarchy
636
639
  self.assertIsNone(self.root.parent)
637
640
  self.assertEqual(self.parent.parent, self.root)
638
641
  self.assertEqual(self.child1.parent, self.parent)
639
642
 
640
- # Delete the parent (/25); child1/child2 now have root (/24) as their parent.
643
+ # Delete the parent (/24); child1/child2 now have root (/16) as their parent.
641
644
  num_deleted, _ = self.parent.delete()
642
645
  self.assertEqual(num_deleted, 1)
643
646
 
@@ -648,9 +651,14 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
648
651
  self.assertEqual(self.child2.parent, self.root)
649
652
  self.assertEqual(list(self.child1.ancestors()), [self.root])
650
653
 
651
- # Add /25 back in as a parent and assert that child1/child2 now have it as their parent, and
652
- # /24 is its parent.
653
- self.parent.save() # This creates another Prefix using the same instance.
654
+ # Add /24 back in as a parent and assert that child1/child2 now have it as their parent, and
655
+ # /16 is its parent.
656
+ self.parent = Prefix.objects.create(
657
+ prefix="101.102.103.0/24",
658
+ status=self.status,
659
+ namespace=self.namespace,
660
+ type=PrefixTypeChoices.TYPE_CONTAINER,
661
+ )
654
662
  self.child1.refresh_from_db()
655
663
  self.child2.refresh_from_db()
656
664
  self.assertEqual(self.child1.parent, self.parent)
@@ -687,13 +695,266 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
687
695
 
688
696
  # Add /25 back in as a parent and assert that child1/child2 now have it as their parent, and
689
697
  # /24 is its parent.
690
- parent.save() # This creates another Prefix using the same instance.
698
+ parent = Prefix.objects.create(
699
+ prefix="101.102.0.0/25", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
700
+ )
691
701
  child1.refresh_from_db()
692
702
  child2.refresh_from_db()
693
703
  self.assertEqual(child1.parent, parent)
694
704
  self.assertEqual(child2.parent, parent)
695
705
  self.assertEqual(list(child1.ancestors()), [root, parent])
696
706
 
707
+ def test_reparenting_on_field_updates(self):
708
+ """Test that reparenting occurs when network, prefix_length, etc. are updated."""
709
+ self.assertIsNone(self.root.parent)
710
+ self.assertEqual(self.parent.parent, self.root)
711
+ self.assertEqual(self.child1.parent, self.parent)
712
+ self.assertEqual(self.child2.parent, self.parent)
713
+
714
+ ip1 = IPAddress.objects.create(address="101.102.103.127/32", status=self.status, namespace=self.namespace)
715
+ ip2 = IPAddress.objects.create(address="101.102.103.128/32", status=self.status, namespace=self.namespace)
716
+ self.assertEqual(ip1.parent, self.parent)
717
+ self.assertEqual(ip2.parent, self.parent)
718
+
719
+ with self.subTest("Decrease prefix_length, gaining children"):
720
+ self.child1.prefix_length = 25
721
+ self.child1.save()
722
+ self.child1.refresh_from_db()
723
+ self.child2.refresh_from_db()
724
+ self.assertEqual(self.child2.parent, self.child1)
725
+ ip1.refresh_from_db()
726
+ self.assertEqual(ip1.parent, self.child1)
727
+
728
+ with self.subTest("Increase prefix_length, losing children"):
729
+ self.child1.prefix_length = 26
730
+ self.child1.save()
731
+ self.child1.refresh_from_db()
732
+ self.child2.refresh_from_db()
733
+ self.assertEqual(self.child2.parent, self.parent)
734
+ ip1.refresh_from_db()
735
+ self.assertEqual(ip1.parent, self.parent)
736
+
737
+ with self.subTest("Broaden prefix, becoming parent of former parent"):
738
+ self.parent.prefix = "101.0.0.0/8"
739
+ self.parent.save()
740
+ self.assertIsNone(self.parent.parent)
741
+ # Former root is now a child of parent
742
+ self.root.refresh_from_db()
743
+ self.assertEqual(self.root.parent, self.parent)
744
+ # Former children are now children of former root
745
+ self.child1.refresh_from_db()
746
+ self.assertEqual(self.child1.parent, self.root)
747
+ self.child2.refresh_from_db()
748
+ self.assertEqual(self.child2.parent, self.root)
749
+ ip1.refresh_from_db()
750
+ self.assertEqual(ip1.parent, self.root)
751
+ ip2.refresh_from_db()
752
+ self.assertEqual(ip2.parent, self.root)
753
+
754
+ with self.subTest("Narrow prefix, becoming child of former child"):
755
+ self.parent.prefix = "101.102.103.0/24"
756
+ self.parent.save()
757
+ self.assertEqual(self.parent.parent, self.root)
758
+ # Former root is now again root
759
+ self.root.refresh_from_db()
760
+ self.assertIsNone(self.root.parent)
761
+ # Former children are again children of parent
762
+ self.child1.refresh_from_db()
763
+ self.assertEqual(self.child1.parent, self.parent)
764
+ self.child2.refresh_from_db()
765
+ self.assertEqual(self.child2.parent, self.parent)
766
+ ip1.refresh_from_db()
767
+ self.assertEqual(ip1.parent, self.parent)
768
+ ip2.refresh_from_db()
769
+ self.assertEqual(ip2.parent, self.parent)
770
+
771
+ with self.subTest("Change former root on multiple dimensions"):
772
+ self.root.network = "101.102.103.0"
773
+ self.root.prefix_length = 25
774
+ self.root.save()
775
+ self.assertEqual(self.root.parent, self.parent)
776
+ self.parent.refresh_from_db()
777
+ self.assertEqual(self.parent.parent, None)
778
+ self.child1.refresh_from_db()
779
+ self.assertEqual(self.child1.parent, self.root)
780
+ self.child2.refresh_from_db()
781
+ self.assertEqual(self.child2.parent, self.root)
782
+ ip1.refresh_from_db()
783
+ self.assertEqual(ip1.parent, self.root)
784
+ ip2.refresh_from_db()
785
+ self.assertEqual(ip2.parent, self.parent)
786
+
787
+ with self.subTest("Reclaim root position"):
788
+ self.root.network = "101.0.0.0"
789
+ self.root.prefix_length = 8
790
+ self.root.save()
791
+ self.assertIsNone(self.root.parent)
792
+ self.parent.refresh_from_db()
793
+ self.assertEqual(self.parent.parent, self.root)
794
+ self.child1.refresh_from_db()
795
+ self.assertEqual(self.child1.parent, self.parent)
796
+ self.child2.refresh_from_db()
797
+ self.assertEqual(self.child2.parent, self.parent)
798
+ ip1.refresh_from_db()
799
+ self.assertEqual(ip1.parent, self.parent)
800
+ ip2.refresh_from_db()
801
+ self.assertEqual(ip2.parent, self.parent)
802
+
803
+ def test_clean_fails_if_would_orphan_ips(self):
804
+ """Test that clean() fails if reparenting would orphan IPs."""
805
+ self.ip = IPAddress.objects.create(address="101.102.1.1/32", status=self.status, namespace=self.namespace)
806
+ self.assertEqual(self.ip.parent, self.root)
807
+ with self.assertRaises(ValidationError) as cm:
808
+ self.root.prefix = "102.103.0.0/16"
809
+ self.root.clean()
810
+ self.assertIn(
811
+ f"1 existing IP addresses (including {self.ip.host}) would no longer have a valid parent", str(cm.exception)
812
+ )
813
+ self.root.refresh_from_db()
814
+ self.ip2 = IPAddress.objects.create(address="101.102.1.2/32", status=self.status, namespace=self.namespace)
815
+ self.assertEqual(self.ip2.parent, self.root)
816
+ with self.assertRaises(ValidationError) as cm:
817
+ self.root.prefix = "102.103.0.0/16"
818
+ self.root.clean()
819
+ self.assertIn(
820
+ f"2 existing IP addresses (including {self.ip.host}) would no longer have a valid parent",
821
+ str(cm.exception),
822
+ )
823
+
824
+ def test_clean_fails_if_namespace_changed_and_vrfs_involved(self):
825
+ vrf = VRF.objects.create(name="VRF Red", namespace=self.namespace)
826
+ vrf.add_prefix(self.root)
827
+
828
+ new_namespace = Namespace.objects.exclude(id=self.namespace.id).first()
829
+
830
+ self.root.namespace = new_namespace
831
+ with self.assertRaises(ValidationError) as cm:
832
+ self.root.clean()
833
+ self.assertIn("Cannot move to a different Namespace while associated to VRFs", str(cm.exception))
834
+
835
+ vrf.remove_prefix(self.root)
836
+ self.root.clean()
837
+
838
+ vrf.add_prefix(self.parent)
839
+ with self.assertRaises(ValidationError) as cm:
840
+ self.root.clean()
841
+ self.assertIn(
842
+ "Cannot move to a different Namespace with descendant Prefixes associated to VRFs", str(cm.exception)
843
+ )
844
+
845
+ def test_namespace_change_success_updates_descendants_and_claims_new_children(self):
846
+ new_namespace = Namespace.objects.exclude(id=self.namespace.id).first()
847
+ new_catchall = Prefix.objects.create(prefix="0.0.0.0/0", status=self.status, namespace=new_namespace)
848
+ new_parent = Prefix.objects.create(prefix="101.102.200.0/24", status=self.status, namespace=new_namespace)
849
+ new_child = Prefix.objects.create(prefix="101.102.103.64/26", status=self.status, namespace=new_namespace)
850
+ new_grandchild = Prefix.objects.create(prefix="101.102.103.0/27", status=self.status, namespace=new_namespace)
851
+ new_ip = IPAddress.objects.create(address="101.102.150.200/32", status=self.status, namespace=new_namespace)
852
+
853
+ # Before:
854
+ # self.namespace
855
+ # self.root 101.102.0.0/16
856
+ # self.parent 101.102.103.0/24
857
+ # self.child1 101.102.103.0/26
858
+ # self.child2 101.102.103.104/32
859
+ # new_namespace
860
+ # new_catchall 0.0.0.0/0
861
+ # new_grandchild 101.102.103.0/27
862
+ # new_child 101.102.103.64/26
863
+ # new_ip 101.102.150.200/32
864
+ # new_parent 101.102.200.0/24
865
+ #
866
+ # After:
867
+ # new_namespace
868
+ # new_catchall 0.0.0.0/0
869
+ # self.root 101.102.0.0/16
870
+ # self.parent 101.102.103.0/24
871
+ # self.child1 101.102.103.0/26
872
+ # new_grandchild 101.102.103.0/27
873
+ # new_child 101.102.103.64/26
874
+ # self.child2 101.102.103.104/32
875
+ # new_ip 101.102.150.200/32
876
+ # new_parent 101.102.200.0/24
877
+
878
+ self.root.namespace = new_namespace
879
+ self.root.save()
880
+ self.assertEqual(self.root.namespace, new_namespace)
881
+ self.assertEqual(self.root.parent, new_catchall) # automatically updated
882
+ self.parent.refresh_from_db()
883
+ self.assertEqual(self.parent.namespace, new_namespace) # automatically updated
884
+ self.assertEqual(self.parent.parent, self.root) # unchanged
885
+ self.child1.refresh_from_db()
886
+ self.assertEqual(self.child1.namespace, new_namespace) # automatically updated
887
+ self.assertEqual(self.child1.parent, self.parent) # unchanged
888
+ self.child2.refresh_from_db()
889
+ self.assertEqual(self.child2.namespace, new_namespace) # automatically updated
890
+ self.assertEqual(self.child2.parent, new_child) # automatically updated
891
+ new_parent.refresh_from_db()
892
+ self.assertEqual(new_parent.namespace, new_namespace) # unchanged
893
+ self.assertEqual(new_parent.parent, self.root) # automatically updated
894
+ new_child.refresh_from_db()
895
+ self.assertEqual(new_child.namespace, new_namespace) # unchanged
896
+ self.assertEqual(new_child.parent, self.parent) # automatically updated
897
+ new_grandchild.refresh_from_db()
898
+ self.assertEqual(new_grandchild.namespace, new_namespace) # unchanged
899
+ self.assertEqual(new_grandchild.parent, self.child1) # automatically updated
900
+ new_ip.refresh_from_db()
901
+ self.assertEqual(new_ip.parent, self.root)
902
+
903
+ def test_namespace_change_results_in_merge_collisions(self):
904
+ new_namespace = Namespace.objects.exclude(id=self.namespace.id).first()
905
+ new_root = Prefix.objects.create(prefix="101.102.0.0/16", status=self.status, namespace=new_namespace)
906
+
907
+ self.root.namespace = new_namespace
908
+ with self.assertRaises(IntegrityError):
909
+ self.root.save()
910
+ self.root.refresh_from_db()
911
+ self.assertEqual(self.root.namespace, self.namespace)
912
+ self.parent.refresh_from_db()
913
+ self.assertEqual(self.parent.namespace, self.namespace)
914
+ self.child1.refresh_from_db()
915
+ self.assertEqual(self.child1.namespace, self.namespace)
916
+ self.child2.refresh_from_db()
917
+ self.assertEqual(self.child2.namespace, self.namespace)
918
+
919
+ new_root.delete()
920
+ new_parent = Prefix.objects.create(prefix="101.102.103.0/24", status=self.status, namespace=new_namespace)
921
+
922
+ self.root.namespace = new_namespace
923
+ with self.assertRaises(IntegrityError):
924
+ self.root.save()
925
+ self.root.refresh_from_db()
926
+ self.assertEqual(self.root.namespace, self.namespace)
927
+ self.parent.refresh_from_db()
928
+ self.assertEqual(self.parent.namespace, self.namespace)
929
+ self.child1.refresh_from_db()
930
+ self.assertEqual(self.child1.namespace, self.namespace)
931
+ self.child2.refresh_from_db()
932
+ self.assertEqual(self.child2.namespace, self.namespace)
933
+
934
+ new_parent.delete()
935
+
936
+ existing_ip = IPAddress.objects.create(address="101.102.103.1/32", status=self.status, namespace=self.namespace)
937
+ new_prefix = Prefix.objects.create(prefix="0.0.0.0/0", status=self.status, namespace=new_namespace)
938
+ new_ip = IPAddress.objects.create(address="101.102.103.1/32", status=self.status, namespace=new_namespace)
939
+ self.assertEqual(new_ip.parent, new_prefix)
940
+
941
+ self.root.namespace = new_namespace
942
+ with self.assertRaises(IntegrityError):
943
+ self.root.save()
944
+ self.root.refresh_from_db()
945
+ self.assertIsNone(self.root.parent)
946
+ self.assertEqual(self.root.namespace, self.namespace)
947
+ self.parent.refresh_from_db()
948
+ self.assertEqual(self.parent.namespace, self.namespace)
949
+ self.child1.refresh_from_db()
950
+ self.assertEqual(self.child1.namespace, self.namespace)
951
+ self.child2.refresh_from_db()
952
+ self.assertEqual(self.child2.namespace, self.namespace)
953
+ existing_ip.refresh_from_db()
954
+ self.assertEqual(existing_ip.parent, self.child1)
955
+ new_ip.refresh_from_db()
956
+ self.assertEqual(new_ip.parent, new_prefix)
957
+
697
958
  def test_descendants(self):
698
959
  prefixes = (
699
960
  Prefix.objects.create(
@@ -901,8 +1162,10 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
901
1162
  self.assertEqual(slash25.get_utilization(), (4, 128))
902
1163
 
903
1164
  # When the pool does not overlap with broadcast or network address, the denominator decrements by 2
904
- pool.network = "10.0.0.132"
905
- pool.save()
1165
+ pool.delete()
1166
+ pool = Prefix.objects.create(
1167
+ prefix="10.0.0.132/30", type=PrefixTypeChoices.TYPE_POOL, status=self.status, namespace=self.namespace
1168
+ )
906
1169
  self.assertEqual(slash25.get_utilization(), (4, 126))
907
1170
 
908
1171
  # Further distinguishing between get_child_ips() and get_all_ips():
@@ -1026,86 +1289,10 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
1026
1289
  Prefix.objects.create(
1027
1290
  prefix="11.0.0.0/24", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
1028
1291
  )
1029
- # 3.0 TODO: replace with the commented below once type enforcement is enabled
1030
- # pool_prefix = Prefix.objects.create(
1031
1292
  Prefix.objects.create(
1032
1293
  prefix="12.0.0.0/24", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
1033
1294
  )
1034
1295
 
1035
- # 3.0 TODO: uncomment the below tests once type enforcement is enabled
1036
-
1037
- # with self.assertRaises(ValidationError, msg="Network prefix parent cannot be a network"):
1038
- # Prefix.objects.create(
1039
- # prefix="11.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
1040
- # )
1041
-
1042
- # with self.assertRaises(ValidationError, msg="Network prefix parent cannot be a pool"):
1043
- # Prefix.objects.create(
1044
- # prefix="12.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
1045
- # )
1046
-
1047
- # with self.assertRaises(ValidationError, msg="Container prefix parent cannot be a network"):
1048
- # Prefix.objects.create(
1049
- # prefix="11.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
1050
- # )
1051
-
1052
- # with self.assertRaises(ValidationError, msg="Container prefix parent cannot be a pool"):
1053
- # Prefix.objects.create(
1054
- # prefix="12.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
1055
- # )
1056
-
1057
- # with self.assertRaises(ValidationError, msg="Pool prefix parent cannot be a container"):
1058
- # Prefix.objects.create(
1059
- # prefix="10.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
1060
- # )
1061
-
1062
- # with self.assertRaises(ValidationError, msg="Pool prefix parent cannot be a pool"):
1063
- # Prefix.objects.create(
1064
- # prefix="12.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
1065
- # )
1066
-
1067
- # with self.assertRaises(
1068
- # ValidationError, msg="Test that an invalid parent cannot be created (network parenting container)"
1069
- # ):
1070
- # Prefix.objects.create(
1071
- # prefix="10.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
1072
- # )
1073
-
1074
- # with self.assertRaises(
1075
- # ValidationError, msg="Test that an invalid parent cannot be created (pool parenting container)"
1076
- # ):
1077
- # Prefix.objects.create(
1078
- # prefix="10.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
1079
- # )
1080
-
1081
- # with self.assertRaises(
1082
- # ValidationError, msg="Test that an invalid parent cannot be created (network parenting network)"
1083
- # ):
1084
- # Prefix.objects.create(
1085
- # prefix="11.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
1086
- # )
1087
-
1088
- # with self.assertRaises(
1089
- # ValidationError, msg="Test that an invalid parent cannot be created (pool parenting network)"
1090
- # ):
1091
- # Prefix.objects.create(
1092
- # prefix="11.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
1093
- # )
1094
-
1095
- # with self.assertRaises(
1096
- # ValidationError, msg="Test that an invalid parent cannot be created (container parenting pool)"
1097
- # ):
1098
- # Prefix.objects.create(
1099
- # prefix="12.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
1100
- # )
1101
-
1102
- # with self.assertRaises(
1103
- # ValidationError, msg="Test that an invalid parent cannot be created (pool parenting pool)"
1104
- # ):
1105
- # Prefix.objects.create(
1106
- # prefix="12.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
1107
- # )
1108
-
1109
1296
  with self.subTest("Test that valid parents can be created"):
1110
1297
  Prefix.objects.create(
1111
1298
  prefix="12.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
@@ -1125,14 +1312,6 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
1125
1312
  prefix="10.0.0.0/26", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
1126
1313
  )
1127
1314
 
1128
- # 3.0 TODO: uncomment once type enforcement is enabled
1129
- # with self.assertRaises(
1130
- # ValidationError,
1131
- # msg="Test that modifying a prefix's type fails if it would result in an invalid parent/child relationship",
1132
- # ):
1133
- # pool_prefix.type = PrefixTypeChoices.TYPE_NETWORK
1134
- # pool_prefix.validated_save()
1135
-
1136
1315
  with self.subTest(
1137
1316
  "Test that modifying a prefix's type is allowed if it does not create an invalid relationship"
1138
1317
  ):
@@ -1159,13 +1338,6 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
1159
1338
  prefix="10.0.0.0/26", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
1160
1339
  )
1161
1340
 
1162
- # 3.0 TODO: uncomment once type enforcement is enabled
1163
- # with self.assertRaises(
1164
- # ProtectedError,
1165
- # msg="Test that deleting a network prefix that would make a pool prefix's parent a container raises a ProtectedError",
1166
- # ):
1167
- # network.delete()
1168
-
1169
1341
  with self.subTest("Test that deleting a parent prefix properly reparents the child prefixes"):
1170
1342
  container.delete()
1171
1343
  root.refresh_from_db()
@@ -1178,18 +1350,11 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
1178
1350
  ip = IPAddress.objects.create(address="10.0.0.1/32", status=self.status, namespace=namespace)
1179
1351
 
1180
1352
  with self.subTest("Test that deleting a pool prefix containing IPs succeeds"):
1181
- self.assertEqual(ip.parent, pool) # 3.0 TODO: change this to ", network)" once IP-to-pool is disallowed
1353
+ self.assertEqual(ip.parent, pool)
1182
1354
  pool.delete()
1183
1355
  ip.refresh_from_db()
1184
1356
  self.assertEqual(ip.parent, network)
1185
1357
 
1186
- # 3.0 TODO: uncomment once type enforcement is enabled
1187
- # with self.assertRaises(
1188
- # ProtectedError,
1189
- # msg="Test that deleting a network prefix that would make an IP's parent a container raises a ProtectedError",
1190
- # ):
1191
- # network.delete()
1192
-
1193
1358
  with self.subTest("Test that deleting the root prefix succeeds"):
1194
1359
  root.delete()
1195
1360
  network.refresh_from_db()
@@ -1433,17 +1598,11 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
1433
1598
  prefix="12.0.0.0/24", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
1434
1599
  )
1435
1600
 
1436
- # 3.0 TODO: uncomment once type enforcement is enabled
1437
- # with self.assertRaises(ValidationError, msg="IP Address parent cannot be a container"):
1438
- # IPAddress.objects.create(address="10.0.0.1/32", status=self.status, namespace=namespace)
1439
-
1440
- # with self.assertRaises(Prefix.DoesNotExist, msg="IP Address parent cannot be a pool"):
1441
- # IPAddress.objects.create(address="12.0.0.1/32", status=self.status, namespace=namespace)
1442
-
1443
1601
  with self.assertRaises(ValidationError) as err:
1444
1602
  IPAddress.objects.create(address="13.0.0.1/32", status=self.status, namespace=namespace)
1445
1603
  self.assertEqual(
1446
- err.exception.message_dict["namespace"][0], "No suitable parent Prefix exists in this Namespace"
1604
+ err.exception.message_dict["namespace"][0],
1605
+ "No suitable parent Prefix for 13.0.0.1 exists in Namespace test_parenting_constraints",
1447
1606
  )
1448
1607
 
1449
1608
  with self.subTest("Test that IP address can be assigned to a valid parent"):
@@ -1483,7 +1642,7 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
1483
1642
  self.assertIn("namespace", err.exception.message_dict)
1484
1643
  self.assertEqual(
1485
1644
  err.exception.message_dict["namespace"][0],
1486
- "No suitable parent Prefix exists in this Namespace",
1645
+ "No suitable parent Prefix for 1976:2023::1 exists in Namespace Global",
1487
1646
  )
1488
1647
 
1489
1648
  # Appropriate parent exists in the default namespace --> no error
@@ -1523,6 +1682,15 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
1523
1682
  ip.validated_save()
1524
1683
  self.assertEqual(ip.parent, prefixes[0])
1525
1684
 
1685
+ def test_change_host(self):
1686
+ ip = IPAddress.objects.create(address="192.0.2.1/32", status=self.status, namespace=self.namespace)
1687
+ self.assertEqual(ip.parent, self.prefix)
1688
+
1689
+ ip.host = "192.168.1.1"
1690
+ with self.assertRaises(ValidationError) as cm:
1691
+ ip.validated_save()
1692
+ self.assertIn("Host address cannot be changed once created", str(cm.exception))
1693
+
1526
1694
  def test_varbinary_ip_fields_with_empty_values_do_not_violate_not_null_constrains(self):
1527
1695
  # Assert that an error is triggered when the host is not provided.
1528
1696
  # Initially, VarbinaryIPField fields with None values are stored as the binary representation of b'',