nautobot 2.4.5__py3-none-any.whl → 2.4.7__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 (410) hide show
  1. nautobot/apps/forms.py +2 -0
  2. nautobot/circuits/templates/circuits/providernetwork.html +1 -1
  3. nautobot/circuits/templates/circuits/providernetwork_retrieve.html +2 -55
  4. nautobot/circuits/views.py +20 -23
  5. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +2 -40
  6. nautobot/cloud/views.py +12 -0
  7. nautobot/core/api/mixins.py +10 -0
  8. nautobot/core/api/urls.py +2 -2
  9. nautobot/core/api/views.py +39 -1
  10. nautobot/core/celery/encoders.py +2 -2
  11. nautobot/core/forms/__init__.py +2 -0
  12. nautobot/core/forms/fields.py +23 -7
  13. nautobot/core/forms/utils.py +1 -0
  14. nautobot/core/forms/widgets.py +18 -0
  15. nautobot/core/jobs/bulk_actions.py +1 -1
  16. nautobot/core/management/commands/generate_test_data.py +1 -1
  17. nautobot/core/models/name_color_content_types.py +9 -0
  18. nautobot/core/models/validators.py +7 -0
  19. nautobot/core/settings.py +0 -14
  20. nautobot/core/settings.yaml +0 -28
  21. nautobot/core/tables.py +6 -1
  22. nautobot/core/templates/generic/object_retrieve.html +1 -1
  23. nautobot/core/templates/widgets/sluginput.html +5 -1
  24. nautobot/core/templatetags/helpers.py +15 -1
  25. nautobot/core/testing/api.py +18 -0
  26. nautobot/core/testing/integration.py +6 -2
  27. nautobot/core/tests/nautobot_config.py +0 -2
  28. nautobot/core/tests/runner.py +17 -140
  29. nautobot/core/tests/test_api.py +4 -4
  30. nautobot/core/tests/test_authentication.py +83 -4
  31. nautobot/core/tests/test_forms.py +11 -8
  32. nautobot/core/tests/test_graphql.py +9 -0
  33. nautobot/core/tests/test_jobs.py +7 -0
  34. nautobot/core/ui/object_detail.py +47 -3
  35. nautobot/core/utils/lookup.py +2 -2
  36. nautobot/dcim/factory.py +2 -0
  37. nautobot/dcim/filters/__init__.py +5 -0
  38. nautobot/dcim/forms.py +27 -1
  39. nautobot/dcim/migrations/0068_alter_softwareimagefile_download_url.py +19 -0
  40. nautobot/dcim/migrations/0069_softwareimagefile_external_integration.py +25 -0
  41. nautobot/dcim/models/devices.py +9 -2
  42. nautobot/dcim/models/locations.py +9 -0
  43. nautobot/dcim/tables/devices.py +1 -0
  44. nautobot/dcim/templates/dcim/device_list.html +1 -1
  45. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  46. nautobot/dcim/templates/dcim/manufacturer.html +1 -63
  47. nautobot/dcim/templates/dcim/module_list.html +1 -1
  48. nautobot/dcim/templates/dcim/moduletype_retrieve.html +1 -1
  49. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +4 -0
  50. nautobot/dcim/tests/integration/test_module_bay_position.py +125 -0
  51. nautobot/dcim/tests/test_api.py +74 -31
  52. nautobot/dcim/tests/test_filters.py +2 -0
  53. nautobot/dcim/tests/test_models.py +78 -0
  54. nautobot/dcim/tests/test_views.py +7 -1
  55. nautobot/dcim/urls.py +1 -45
  56. nautobot/dcim/views.py +35 -66
  57. nautobot/extras/choices.py +4 -0
  58. nautobot/extras/filters/__init__.py +6 -0
  59. nautobot/extras/forms/forms.py +83 -11
  60. nautobot/extras/models/customfields.py +10 -9
  61. nautobot/extras/plugins/marketplace_manifest.yml +18 -0
  62. nautobot/extras/signals.py +43 -4
  63. nautobot/extras/tables.py +4 -5
  64. nautobot/extras/tasks.py +4 -2
  65. nautobot/extras/templates/extras/contact_retrieve.html +1 -58
  66. nautobot/extras/templates/extras/exporttemplate.html +1 -53
  67. nautobot/extras/templates/extras/inc/panel_changelog.html +1 -1
  68. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  69. nautobot/extras/templates/extras/status.html +1 -37
  70. nautobot/extras/templates/extras/team_retrieve.html +1 -58
  71. nautobot/extras/tests/integration/test_notes.py +1 -1
  72. nautobot/extras/tests/test_api.py +22 -7
  73. nautobot/extras/tests/test_changelog.py +4 -4
  74. nautobot/extras/tests/test_customfields.py +27 -0
  75. nautobot/extras/tests/test_plugins.py +19 -13
  76. nautobot/extras/tests/test_relationships.py +9 -0
  77. nautobot/extras/tests/test_tags.py +2 -2
  78. nautobot/extras/tests/test_views.py +37 -6
  79. nautobot/extras/urls.py +3 -100
  80. nautobot/extras/views.py +111 -133
  81. nautobot/ipam/tables.py +7 -2
  82. nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -41
  83. nautobot/ipam/templates/ipam/service.html +2 -46
  84. nautobot/ipam/templates/ipam/service_edit.html +1 -17
  85. nautobot/ipam/templates/ipam/service_retrieve.html +7 -0
  86. nautobot/ipam/tests/migration/__init__.py +0 -0
  87. nautobot/ipam/tests/migration/test_migrations.py +510 -0
  88. nautobot/ipam/tests/test_api.py +66 -36
  89. nautobot/ipam/tests/test_filters.py +0 -10
  90. nautobot/ipam/tests/test_views.py +44 -2
  91. nautobot/ipam/urls.py +2 -47
  92. nautobot/ipam/utils/migrations.py +185 -152
  93. nautobot/ipam/utils/testing.py +177 -0
  94. nautobot/ipam/views.py +95 -157
  95. nautobot/project-static/css/base.css +5 -0
  96. nautobot/project-static/docs/404.html +0 -2
  97. nautobot/project-static/docs/apps/index.html +0 -2
  98. nautobot/project-static/docs/apps/nautobot-apps.html +0 -2
  99. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +0 -2
  100. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +0 -2
  101. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +0 -2
  102. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +0 -2
  103. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +0 -2
  104. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +0 -2
  105. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +0 -2
  106. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +0 -2
  107. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +0 -2
  108. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +0 -2
  109. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +0 -2
  110. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +0 -2
  111. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +62 -2
  112. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +0 -2
  113. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +0 -2
  114. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +47 -2
  115. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +0 -2
  116. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +0 -2
  117. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +18 -2
  118. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +0 -2
  119. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +70 -5
  120. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +0 -2
  121. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +0 -2
  122. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +0 -2
  123. nautobot/project-static/docs/development/apps/api/configuration-view.html +0 -2
  124. nautobot/project-static/docs/development/apps/api/database-backend-config.html +0 -2
  125. nautobot/project-static/docs/development/apps/api/models/django-admin.html +0 -2
  126. nautobot/project-static/docs/development/apps/api/models/global-search.html +0 -2
  127. nautobot/project-static/docs/development/apps/api/models/graphql.html +0 -2
  128. nautobot/project-static/docs/development/apps/api/models/index.html +0 -2
  129. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +0 -2
  130. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +0 -2
  131. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +0 -2
  132. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +0 -2
  133. nautobot/project-static/docs/development/apps/api/platform-features/index.html +0 -2
  134. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +0 -2
  135. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +0 -2
  136. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +0 -2
  137. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +0 -2
  138. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +0 -2
  139. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +0 -2
  140. nautobot/project-static/docs/development/apps/api/prometheus.html +0 -2
  141. nautobot/project-static/docs/development/apps/api/setup.html +0 -2
  142. nautobot/project-static/docs/development/apps/api/testing.html +0 -89
  143. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +0 -2
  144. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +0 -2
  145. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +0 -2
  146. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +0 -2
  147. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +0 -2
  148. nautobot/project-static/docs/development/apps/api/views/base-template.html +0 -2
  149. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +0 -2
  150. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +0 -2
  151. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +0 -2
  152. nautobot/project-static/docs/development/apps/api/views/index.html +0 -2
  153. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +0 -2
  154. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +0 -2
  155. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +0 -2
  156. nautobot/project-static/docs/development/apps/api/views/notes.html +0 -2
  157. nautobot/project-static/docs/development/apps/api/views/rest-api.html +0 -2
  158. nautobot/project-static/docs/development/apps/api/views/urls.html +0 -2
  159. nautobot/project-static/docs/development/apps/index.html +0 -2
  160. nautobot/project-static/docs/development/apps/migration/code-updates.html +0 -2
  161. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +1 -3
  162. nautobot/project-static/docs/development/apps/migration/from-v1.html +0 -2
  163. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +0 -2
  164. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +0 -2
  165. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +0 -2
  166. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +0 -2
  167. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +0 -2
  168. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +0 -2
  169. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +0 -2
  170. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +0 -2
  171. nautobot/project-static/docs/development/apps/porting-from-netbox.html +0 -2
  172. nautobot/project-static/docs/development/core/application-registry.html +0 -2
  173. nautobot/project-static/docs/development/core/best-practices.html +3 -5
  174. nautobot/project-static/docs/development/core/bootstrap-ui.html +0 -2
  175. nautobot/project-static/docs/development/core/caching.html +0 -2
  176. nautobot/project-static/docs/development/core/controllers.html +0 -2
  177. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +0 -2
  178. nautobot/project-static/docs/development/core/generic-views.html +0 -2
  179. nautobot/project-static/docs/development/core/getting-started.html +78 -109
  180. nautobot/project-static/docs/development/core/homepage.html +0 -2
  181. nautobot/project-static/docs/development/core/index.html +0 -2
  182. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +0 -2
  183. nautobot/project-static/docs/development/core/model-checklist.html +0 -2
  184. nautobot/project-static/docs/development/core/model-features.html +0 -2
  185. nautobot/project-static/docs/development/core/natural-keys.html +0 -2
  186. nautobot/project-static/docs/development/core/navigation-menu.html +0 -2
  187. nautobot/project-static/docs/development/core/release-checklist.html +1 -3
  188. nautobot/project-static/docs/development/core/role-internals.html +0 -2
  189. nautobot/project-static/docs/development/core/settings.html +0 -2
  190. nautobot/project-static/docs/development/core/style-guide.html +1 -3
  191. nautobot/project-static/docs/development/core/templates.html +0 -2
  192. nautobot/project-static/docs/development/core/testing.html +24 -200
  193. nautobot/project-static/docs/development/core/ui-component-framework.html +0 -2
  194. nautobot/project-static/docs/development/core/user-preferences.html +0 -2
  195. nautobot/project-static/docs/development/index.html +0 -2
  196. nautobot/project-static/docs/development/jobs/index.html +0 -2
  197. nautobot/project-static/docs/development/jobs/migration/from-v1.html +0 -2
  198. nautobot/project-static/docs/index.html +0 -2
  199. nautobot/project-static/docs/media/user-guide/administration/getting-started/nautobot-cloud.png +0 -0
  200. nautobot/project-static/docs/objects.inv +0 -0
  201. nautobot/project-static/docs/overview/application_stack.html +1 -3
  202. nautobot/project-static/docs/overview/design_philosophy.html +0 -2
  203. nautobot/project-static/docs/release-notes/index.html +0 -2
  204. nautobot/project-static/docs/release-notes/version-1.0.html +0 -2
  205. nautobot/project-static/docs/release-notes/version-1.1.html +0 -2
  206. nautobot/project-static/docs/release-notes/version-1.2.html +0 -2
  207. nautobot/project-static/docs/release-notes/version-1.3.html +0 -2
  208. nautobot/project-static/docs/release-notes/version-1.4.html +0 -2
  209. nautobot/project-static/docs/release-notes/version-1.5.html +0 -2
  210. nautobot/project-static/docs/release-notes/version-1.6.html +0 -2
  211. nautobot/project-static/docs/release-notes/version-2.0.html +0 -2
  212. nautobot/project-static/docs/release-notes/version-2.1.html +0 -2
  213. nautobot/project-static/docs/release-notes/version-2.2.html +0 -2
  214. nautobot/project-static/docs/release-notes/version-2.3.html +0 -2
  215. nautobot/project-static/docs/release-notes/version-2.4.html +364 -3
  216. nautobot/project-static/docs/search/search_index.json +1 -1
  217. nautobot/project-static/docs/sitemap.xml +290 -290
  218. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  219. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +0 -2
  220. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +0 -2
  221. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +0 -2
  222. nautobot/project-static/docs/user-guide/administration/configuration/index.html +0 -2
  223. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +0 -2
  224. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +2 -50
  225. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +0 -2
  226. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +0 -2
  227. nautobot/project-static/docs/user-guide/administration/guides/docker.html +0 -2
  228. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +0 -2
  229. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +71 -2
  230. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +0 -2
  231. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +0 -2
  232. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +0 -2
  233. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +0 -2
  234. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +0 -2
  235. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +0 -2
  236. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +0 -2
  237. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -3
  238. nautobot/project-static/docs/user-guide/administration/installation/index.html +257 -18
  239. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +0 -2
  240. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +0 -2
  241. nautobot/project-static/docs/user-guide/administration/installation/services.html +0 -2
  242. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +0 -2
  243. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +0 -2
  244. nautobot/project-static/docs/user-guide/administration/security/index.html +0 -2
  245. nautobot/project-static/docs/user-guide/administration/security/notices.html +0 -2
  246. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +1 -3
  247. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +0 -2
  248. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +0 -2
  249. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +0 -2
  250. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +0 -2
  251. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +0 -2
  252. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +0 -2
  253. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +0 -2
  254. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +0 -2
  255. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +0 -2
  256. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +2 -4
  257. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +0 -2
  258. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +0 -2
  259. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +0 -2
  260. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +0 -2
  261. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +0 -2
  262. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +0 -2
  263. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +0 -2
  264. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +0 -2
  265. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +0 -2
  266. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +0 -2
  267. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +0 -2
  268. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +0 -2
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +0 -2
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +0 -2
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +0 -2
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +0 -2
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +0 -2
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +0 -2
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +0 -2
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +0 -2
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +0 -2
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +0 -2
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +0 -2
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +0 -2
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +0 -2
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +0 -2
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +0 -2
  284. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +0 -2
  285. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +0 -2
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +0 -2
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +0 -2
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +0 -2
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +0 -2
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +0 -2
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +0 -2
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +0 -2
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +0 -2
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +0 -2
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +0 -2
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +0 -2
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +0 -2
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +0 -2
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +0 -2
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +0 -2
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +0 -2
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +0 -2
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +0 -2
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +0 -2
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +0 -2
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +0 -2
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -2
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +0 -2
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +0 -2
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +0 -2
  311. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +0 -2
  312. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +0 -2
  313. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +0 -2
  314. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +0 -2
  315. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +0 -2
  316. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +0 -2
  317. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +0 -2
  318. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +0 -2
  319. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +0 -2
  320. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +0 -2
  321. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +0 -2
  322. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +0 -2
  323. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +0 -2
  324. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +0 -2
  325. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +0 -2
  326. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +0 -2
  327. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +0 -2
  328. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +0 -2
  329. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +0 -2
  330. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +0 -2
  331. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +0 -2
  332. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +0 -2
  333. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +0 -2
  334. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +0 -2
  335. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +0 -2
  336. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -13
  337. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +0 -2
  338. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +8 -10
  339. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +1 -2
  340. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +0 -2
  341. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +40 -27
  342. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -6
  343. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +1 -3
  344. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +77 -7
  345. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +1 -3
  346. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +0 -3
  347. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +1 -3
  348. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +0 -2
  349. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +0 -2
  350. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +0 -2
  351. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +0 -2
  352. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -2
  353. nautobot/project-static/docs/user-guide/index.html +89 -4
  354. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +0 -2
  355. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +0 -2
  356. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +0 -2
  357. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +0 -2
  358. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +0 -2
  359. nautobot/project-static/docs/user-guide/platform-functionality/events.html +0 -2
  360. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +0 -2
  361. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +0 -2
  362. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +0 -2
  363. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +0 -2
  364. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +0 -2
  365. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +0 -2
  366. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +0 -2
  367. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +0 -2
  368. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +0 -2
  369. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +0 -2
  370. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +0 -2
  371. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -2
  372. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +0 -2
  373. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +0 -2
  374. nautobot/project-static/docs/user-guide/platform-functionality/note.html +0 -2
  375. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +0 -2
  376. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +0 -2
  377. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +0 -2
  378. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +0 -2
  379. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +0 -2
  380. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +0 -2
  381. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +0 -2
  382. nautobot/project-static/docs/user-guide/platform-functionality/role.html +0 -2
  383. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +0 -2
  384. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +0 -2
  385. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +0 -2
  386. nautobot/project-static/docs/user-guide/platform-functionality/status.html +0 -2
  387. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +0 -2
  388. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +0 -2
  389. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +0 -2
  390. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +0 -2
  391. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +207 -124
  392. nautobot/project-static/js/forms.js +88 -37
  393. nautobot/project-static/js/homepage_layout.js +12 -3
  394. nautobot/virtualization/forms.py +20 -0
  395. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -39
  396. nautobot/virtualization/templates/virtualization/clustertype.html +1 -0
  397. nautobot/virtualization/tests/test_api.py +14 -3
  398. nautobot/virtualization/tests/test_views.py +10 -2
  399. nautobot/virtualization/urls.py +10 -93
  400. nautobot/virtualization/views.py +33 -72
  401. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/METADATA +6 -5
  402. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/RECORD +407 -402
  403. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/WHEEL +1 -1
  404. nautobot/core/tests/performance_baselines.yml +0 -8900
  405. nautobot/dcim/templates/dcim/modulebay_create.html +0 -39
  406. nautobot/ipam/tests/test_migrations.py +0 -462
  407. /nautobot/ipam/templates/ipam/{namespace_ipaddresses.html → namespace_ip_addresses.html} +0 -0
  408. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/LICENSE.txt +0 -0
  409. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/NOTICE +0 -0
  410. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/entry_points.txt +0 -0
@@ -1,46 +1,84 @@
1
1
  import collections
2
2
  import sys
3
+ from time import monotonic
3
4
 
4
5
  from django.core.exceptions import ValidationError
5
6
  from django.db import models
6
7
  import netaddr
7
8
 
9
+ from nautobot.ipam.constants import IPV4_BYTE_LENGTH, IPV6_BYTE_LENGTH
10
+
8
11
  BASE_NAME = "Cleanup Namespace"
9
12
  DESCRIPTION = "Created by Nautobot 2.0 IPAM data migrations."
10
13
  GLOBAL_NS = "Global"
11
14
 
12
15
 
16
+ class TimerContextManager:
17
+ def __init__(self, message, indent=""):
18
+ self.message = message
19
+ self.indent = indent
20
+
21
+ def __enter__(self):
22
+ self.start_time = monotonic()
23
+ print(f"{self.indent}>>> {self.message}...")
24
+ return self
25
+
26
+ def __exit__(self, *args, **kwargs):
27
+ self.elapsed_time = monotonic() - self.start_time
28
+ print(f"{self.indent} ... completed (elapsed time: {self.elapsed_time:.1f} seconds)")
29
+
30
+
31
+ def is_prefix(obj):
32
+ return obj.__class__.__name__ == "Prefix"
33
+
34
+
35
+ def is_ipaddress(obj):
36
+ return obj.__class__.__name__ == "IPAddress"
37
+
38
+
13
39
  def process_namespaces(apps, schema_editor):
40
+ """
41
+ Migration entry point for 1.x to 2.x IPAM data migration.
42
+ """
14
43
  print("\n", end="")
15
44
 
16
- # Fail if any interface or vm interface has IPs with different VRFs
17
- check_interface_vrfs(apps)
45
+ with TimerContextManager("Checking whether any Interface or VMInterface has IPs with differing VRFs"):
46
+ check_interface_vrfs(apps)
18
47
 
19
48
  # Prefix Broadcast is a derived field, so we should update it before we start
20
- ensure_correct_prefix_broadcast(apps)
49
+ with TimerContextManager("Verifying all Prefix.broadcast values"):
50
+ ensure_correct_prefix_broadcast(apps)
21
51
 
22
52
  # Cleanup Prefixes and IPAddresses version fields
23
- add_prefix_and_ip_address_version(apps)
53
+ with TimerContextManager("Setting Prefix.version and IPAddress.version values"):
54
+ add_prefix_and_ip_address_version(apps)
24
55
 
25
56
  # VRFs
26
- process_vrfs(apps)
57
+ with TimerContextManager("Processing VRFs"):
58
+ process_vrfs(apps)
27
59
 
28
60
  # IPAddresses
29
- process_ip_addresses(apps)
61
+ with TimerContextManager("Processing IPAddresses"):
62
+ process_ip_addresses(apps)
30
63
 
31
64
  # Prefixes
32
- process_prefix_duplicates(apps)
33
- reparent_prefixes(apps)
65
+ with TimerContextManager("Processing duplicate Prefixes"):
66
+ process_prefix_duplicates(apps)
67
+ with TimerContextManager("Reparenting Prefixes"):
68
+ reparent_prefixes(apps)
34
69
 
35
70
  # Make another pass across all VRFs to duplicate it if it has prefixes
36
71
  # in another namespace (non-unique VRFs with duplicate Prefixes)
37
- copy_vrfs_to_cleanup_namespaces(apps)
72
+ with TimerContextManager("Copying VRFs to cleanup Namespaces as needed"):
73
+ copy_vrfs_to_cleanup_namespaces(apps)
38
74
 
39
75
  # [VM]Interfaces
40
- process_interfaces(apps)
76
+ with TimerContextManager("Processing Interfaces and VM Interfaces"):
77
+ process_interfaces(apps)
41
78
 
42
79
  # VRF-Prefix M2M
43
- process_vrfs_prefixes_m2m(apps)
80
+ with TimerContextManager("Processing VRF to Prefix many-to-many"):
81
+ process_vrfs_prefixes_m2m(apps)
44
82
 
45
83
 
46
84
  def check_interface_vrfs(apps):
@@ -105,36 +143,37 @@ def process_vrfs(apps):
105
143
  Returns:
106
144
  None
107
145
  """
146
+ Namespace = apps.get_model("ipam", "Namespace")
108
147
  VRF = apps.get_model("ipam", "VRF")
148
+
149
+ global_ns = Namespace.objects.get(name=GLOBAL_NS)
109
150
  vrfs = VRF.objects.all().order_by("name", "rd")
110
151
  unique_non_empty_vrfs = vrfs.filter(enforce_unique=True).exclude(ip_addresses__isnull=True, prefixes__isnull=True)
111
152
  # At the point in the migration where we iterate through vrfs in global_ns_vrfs, every vrf that
112
153
  # has already been processed has been moved to a new namespace. Anything left in the global
113
154
  # namespace has yet to be processed which is why we're iterating through this on the second
114
155
  # loop.
115
- global_ns_vrfs = vrfs.filter(namespace__name=GLOBAL_NS)
156
+ global_ns_vrfs = vrfs.filter(namespace=global_ns)
116
157
 
117
158
  # Case 0: VRFs with enforce_unique move to their own Namespace.
118
- for vrf in unique_non_empty_vrfs:
159
+ for vrf in unique_non_empty_vrfs.iterator():
119
160
  if "test" not in sys.argv:
120
161
  print(f">>> Processing migration for VRF {vrf.name!r}, Namespace {vrf.namespace.name!r}")
121
162
  vrf.namespace = create_vrf_namespace(apps, vrf)
122
163
  vrf.save()
123
164
  vrf.prefixes.update(namespace=vrf.namespace)
124
- if "test" not in sys.argv:
125
- print(f" VRF {vrf.name!r} migrated to Namespace {vrf.namespace.name!r}")
165
+ print(f" VRF {vrf.name!r} migrated to Namespace {vrf.namespace.name!r}")
126
166
 
127
167
  # Case 00: VRFs with duplicate names or prefixes move to a Cleanup Namespace.
128
168
  # Case 1 is not included here because it is a no-op.
129
- for vrf in global_ns_vrfs.annotate(prefix_count=models.Count("prefixes")).order_by("-prefix_count"):
169
+ for vrf in global_ns_vrfs.annotate(prefix_count=models.Count("prefixes")).order_by("-prefix_count").iterator():
130
170
  if "test" not in sys.argv:
131
- print(f">>> Processing migration for VRF {vrf.name!r}, Namespace {vrf.namespace.name!r}")
132
- original_namespace = vrf.namespace
133
- vrf.namespace = get_next_vrf_cleanup_namespace(apps, vrf)
134
- vrf.save()
135
- if vrf.namespace != original_namespace:
171
+ print(f">>> Processing migration for VRF {vrf.name!r}, Namespace {global_ns.name!r}")
172
+ vrf.namespace = get_next_vrf_cleanup_namespace(apps, vrf, global_ns=global_ns)
173
+ if vrf.namespace != global_ns:
174
+ vrf.save()
136
175
  vrf.prefixes.update(namespace=vrf.namespace)
137
- print(f" VRF {vrf.name!r} migrated from Namespace {original_namespace.name!r} to {vrf.namespace.name!r}")
176
+ print(f" VRF {vrf.name!r} migrated from Namespace {global_ns.name!r} to {vrf.namespace.name!r}")
138
177
 
139
178
 
140
179
  def add_prefix_and_ip_address_version(apps):
@@ -152,17 +191,21 @@ def add_prefix_and_ip_address_version(apps):
152
191
 
153
192
  if "test" not in sys.argv:
154
193
  print(">>> Populating Prefix.ip_version field")
155
- for pfx in Prefix.objects.all():
156
- cidr = validate_cidr(apps, pfx)
157
- pfx.ip_version = cidr.version
158
- pfx.save()
194
+ Prefix.objects.annotate(address_len=models.functions.Length(models.F("network"))).filter(
195
+ address_len=IPV4_BYTE_LENGTH
196
+ ).update(ip_version=4)
197
+ Prefix.objects.annotate(address_len=models.functions.Length(models.F("network"))).filter(
198
+ address_len=IPV6_BYTE_LENGTH
199
+ ).update(ip_version=6)
159
200
 
160
201
  if "test" not in sys.argv:
161
202
  print(">>> Populating IPAddress.ip_version field")
162
- for ip in IPAddress.objects.all():
163
- cidr = validate_cidr(apps, ip)
164
- ip.ip_version = cidr.version
165
- ip.save()
203
+ IPAddress.objects.annotate(address_len=models.functions.Length(models.F("host"))).filter(
204
+ address_len=IPV4_BYTE_LENGTH
205
+ ).update(ip_version=4)
206
+ IPAddress.objects.annotate(address_len=models.functions.Length(models.F("host"))).filter(
207
+ address_len=IPV6_BYTE_LENGTH
208
+ ).update(ip_version=6)
166
209
 
167
210
 
168
211
  def process_ip_addresses(apps):
@@ -186,53 +229,52 @@ def process_ip_addresses(apps):
186
229
  Namespace = apps.get_model("ipam", "Namespace")
187
230
  Prefix = apps.get_model("ipam", "Prefix")
188
231
 
189
- # Explicitly set the parent for those that were found and save them.
190
- for ip in IPAddress.objects.filter(parent__isnull=True).order_by("-vrf", "-tenant"):
191
- potential_parents = get_closest_parent(apps, ip, Prefix.objects.all())
192
- for prefix in potential_parents:
193
- if not prefix.ip_addresses.filter(host=ip.host).exists():
194
- ip.parent = prefix
232
+ with TimerContextManager("Reparenting individual IPAddresses to a close-enough parent Prefix", indent=" "):
233
+ # For IPs that don't have an exact obvious parent prefix, find close-enough matches.
234
+ # Explicitly set the parent for those that were found and save them.
235
+ for ip in IPAddress.objects.filter(parent__isnull=True).order_by("-vrf", "-tenant").iterator():
236
+ potential_parent = get_closest_parent(ip, Prefix.objects.all())
237
+ if potential_parent is not None:
238
+ ip.parent = potential_parent
195
239
  ip.save()
196
- break
197
-
198
- # For IPs with no discovered parent, create one and assign it to the IP.
199
- global_ns = Namespace.objects.get(name=GLOBAL_NS)
200
- for orphaned_ip in IPAddress.objects.filter(parent__isnull=True):
201
- ip_repr = str(validate_cidr(apps, orphaned_ip))
202
- if "test" not in sys.argv:
203
- print(f">>> Processing Parent migration for orphaned IPAddress {ip_repr!r}")
204
240
 
205
- new_parent_cidr = generate_parent_prefix(apps, orphaned_ip)
206
- network = new_parent_cidr.network
207
- prefix_length = new_parent_cidr.prefixlen
208
- potential_parents = Prefix.objects.filter(network=network, prefix_length=prefix_length).exclude(
209
- ip_addresses__host=orphaned_ip.host
210
- )
211
- if potential_parents.exists():
212
- new_parent = potential_parents.first()
241
+ with TimerContextManager("Reparenting orphaned IPAddresses by creating new Prefixes as needed", indent=" "):
242
+ # For IPs with no discovered parent, create one and assign it to the IP.
243
+ global_ns = Namespace.objects.get(name=GLOBAL_NS)
244
+ for orphaned_ip in IPAddress.objects.filter(parent__isnull=True).select_related("tenant", "vrf").iterator():
245
+ ip_repr = str(validate_cidr(orphaned_ip))
246
+ if "test" not in sys.argv:
247
+ print(f">>> Processing Parent migration for orphaned IPAddress {ip_repr!r}")
213
248
 
214
- else:
215
- broadcast = new_parent_cidr[-1]
216
- # This can result in duplicate Prefixes being created in the global_ns but that will be
217
- # cleaned up subsequently in `process_prefix_duplicates`.
218
- new_parent = Prefix.objects.create(
219
- ip_version=orphaned_ip.ip_version,
220
- network=network,
221
- broadcast=broadcast,
222
- tenant=orphaned_ip.tenant,
223
- vrf=orphaned_ip.vrf,
224
- prefix_length=prefix_length,
225
- namespace=orphaned_ip.vrf.namespace if orphaned_ip.vrf else global_ns,
226
- description=DESCRIPTION,
249
+ new_parent_cidr = generate_parent_prefix(apps, orphaned_ip)
250
+ network = new_parent_cidr.network
251
+ prefix_length = new_parent_cidr.prefixlen
252
+ potential_parents = Prefix.objects.filter(network=network, prefix_length=prefix_length).exclude(
253
+ ip_addresses__host=orphaned_ip.host
227
254
  )
228
- orphaned_ip.parent = new_parent
229
- orphaned_ip.save()
255
+ new_parent = potential_parents.first()
256
+ if new_parent is None:
257
+ broadcast = new_parent_cidr[-1]
258
+ # This can result in duplicate Prefixes being created in the global_ns but that will be
259
+ # cleaned up subsequently in `process_prefix_duplicates`.
260
+ new_parent = Prefix.objects.create(
261
+ ip_version=orphaned_ip.ip_version,
262
+ network=network,
263
+ broadcast=broadcast,
264
+ tenant=orphaned_ip.tenant,
265
+ vrf=orphaned_ip.vrf,
266
+ prefix_length=prefix_length,
267
+ namespace=orphaned_ip.vrf.namespace if orphaned_ip.vrf else global_ns,
268
+ description=DESCRIPTION,
269
+ )
270
+ orphaned_ip.parent = new_parent
271
+ orphaned_ip.save()
230
272
 
231
- parent_repr = str(validate_cidr(apps, new_parent))
232
- if "test" not in sys.argv:
233
- print(
234
- f" IPAddress {ip_repr!r} migrated to Parent Prefix {parent_repr!r} in Namespace {new_parent.namespace.name!r}"
235
- )
273
+ parent_repr = str(validate_cidr(new_parent))
274
+ if "test" not in sys.argv:
275
+ print(
276
+ f" IPAddress {ip_repr!r} migrated to Parent Prefix {parent_repr!r} in Namespace {new_parent.namespace.name!r}"
277
+ )
236
278
 
237
279
  # By this point we should arrive at NO orphaned IPAddress objects.
238
280
  if IPAddress.objects.filter(parent__isnull=True).exists():
@@ -274,7 +316,9 @@ def process_prefix_duplicates(apps):
274
316
  if "test" not in sys.argv:
275
317
  print(f">>> Processing Namespace migration for duplicate Prefix {dupe!r}")
276
318
  network, prefix_length = dupe.split("/")
277
- objects = Prefix.objects.filter(network=network, prefix_length=prefix_length, namespace=ns)
319
+ objects = Prefix.objects.filter(network=network, prefix_length=prefix_length, namespace=ns).select_related(
320
+ "tenant"
321
+ )
278
322
  # Leave the last instance of the Prefix in the original Namespace
279
323
  last_prefix = objects.filter(tenant_id=tenant_ids_sorted.last()).last()
280
324
 
@@ -308,18 +352,14 @@ def reparent_prefixes(apps):
308
352
 
309
353
  if "test" not in sys.argv:
310
354
  print("\n>>> Processing Prefix parents, please standby...")
311
- for pfx in Prefix.objects.all().order_by("-prefix_length", "tenant"):
312
- try:
313
- parent = get_closest_parent(apps, pfx, pfx.namespace.prefixes.all())
314
- if pfx.namespace != parent.namespace:
315
- raise ValidationError("Prefix and parent are in different Namespaces")
355
+ for pfx in Prefix.objects.all().order_by("-prefix_length", "tenant").select_related("namespace").iterator():
356
+ parent = get_closest_parent(pfx, pfx.namespace.prefixes.all())
357
+ if parent is not None:
316
358
  # TODO: useful but potentially very noisy. Do migrations have a verbosity option?
317
359
  # if "test" not in sys.argv:
318
360
  # print(f">>> {pfx.network}/{pfx.prefix_length} parent: {parent.network}/{parent.prefix_length}")
319
361
  pfx.parent = parent
320
362
  pfx.save()
321
- except Prefix.DoesNotExist:
322
- continue
323
363
 
324
364
 
325
365
  def copy_vrfs_to_cleanup_namespaces(apps):
@@ -339,14 +379,11 @@ def copy_vrfs_to_cleanup_namespaces(apps):
339
379
  VRF = apps.get_model("ipam", "VRF")
340
380
  Namespace = apps.get_model("ipam", "Namespace")
341
381
 
342
- for vrf in VRF.objects.all():
343
- if not vrf.prefixes.exclude(namespace=vrf.namespace).exists():
344
- continue
345
-
382
+ for vrf in VRF.objects.select_related("namespace", "tenant").iterator():
346
383
  namespaces = (
347
384
  vrf.prefixes.exclude(namespace=vrf.namespace).order_by().values_list("namespace", flat=True).distinct()
348
385
  )
349
- for namespace_pk in namespaces:
386
+ for namespace_pk in namespaces.iterator():
350
387
  namespace = Namespace.objects.get(pk=namespace_pk)
351
388
  if "test" not in sys.argv:
352
389
  print(f">>> Copying VRF {vrf.name!r} to namespace {namespace.name!r}")
@@ -386,11 +423,11 @@ def process_interfaces(apps):
386
423
  # Case 2: Interface has one or more IP address assigned to it with no more than 1 distinct associated VRF (none is excluded)
387
424
  # The interface's VRF foreign key should be set to the VRF of any related IP Address with a non-null VRF.
388
425
  # The interface's parent device or virtual machine should adopt an assocation to the VRF (VRFDeviceAssignment) as well.
389
- for ifc in ip_interfaces:
426
+ for ifc in ip_interfaces.select_related("device").iterator():
390
427
  if "test" not in sys.argv:
391
428
  print(f">>> Processing VRF migration for numbered Interface {ifc.name!r}")
392
429
  # Set the Interface VRF to that of the first assigned IPAddress.
393
- first_ip = ifc.ip_addresses.filter(vrf__isnull=False).first()
430
+ first_ip = ifc.ip_addresses.filter(vrf__isnull=False).select_related("vrf").first()
394
431
 
395
432
  ifc_vrf = first_ip.vrf
396
433
  ifc.vrf = ifc_vrf
@@ -403,11 +440,11 @@ def process_interfaces(apps):
403
440
  print(f" VRF {ifc_vrf.name!r} migrated from IPAddress {first_ip.host!r} to Interface {ifc.name!r}")
404
441
 
405
442
  # VirtualMachine should adopt an association to the VRF (VRFDeviceAssignment) as well.
406
- for ifc in ip_vminterfaces:
443
+ for ifc in ip_vminterfaces.select_related("virtual_machine").iterator():
407
444
  if "test" not in sys.argv:
408
445
  print(f">>> Processing VRF migration for numbered VMInterface {ifc.name!r}")
409
446
  # Set the VMInterface VRF to that of the first assigned IPAddress.
410
- first_ip = ifc.ip_addresses.filter(vrf__isnull=False).first()
447
+ first_ip = ifc.ip_addresses.filter(vrf__isnull=False).select_related("vrf").first()
411
448
 
412
449
  ifc_vrf = first_ip.vrf
413
450
  ifc.vrf = ifc_vrf
@@ -437,7 +474,7 @@ def process_vrfs_prefixes_m2m(apps):
437
474
 
438
475
  vrfs_with_prefixes = VRF.objects.filter(prefixes__isnull=False).order_by().distinct()
439
476
 
440
- for vrf in vrfs_with_prefixes:
477
+ for vrf in vrfs_with_prefixes.iterator():
441
478
  if "test" not in sys.argv:
442
479
  print(f" Converting Prefix relationships to VRF {vrf.name} to M2M.")
443
480
  vrf.prefixes_m2m.set(vrf.prefixes.all())
@@ -448,28 +485,30 @@ def get_prefixes(qs):
448
485
  Given a queryset, return the prefixes as 2-tuples of (network, prefix_length).
449
486
 
450
487
  Args:
451
- qs (QuerySet): QuerySet of Prefix objects.
488
+ qs (QuerySet, set): QuerySet of Prefix objects, or set of values already processed by this function
452
489
 
453
490
  Returns:
454
- list
491
+ set
455
492
  """
456
- return sorted(qs.values_list("network", "prefix_length"))
493
+ if isinstance(qs, set):
494
+ return qs
495
+ return set(qs.values_list("network", "prefix_length"))
457
496
 
458
497
 
459
498
  def compare_prefix_querysets(a, b):
460
499
  """
461
- Compare two QuerySets of Prefix objects and return the set intersection of both.
500
+ Compare two QuerySets of Prefix objects and return whether the set intersection has any common networks.
462
501
 
463
502
  Args:
464
- a (QuerySet): Left-side QuerySet
465
- b (QuerySet): Right-side QuerySet
503
+ a (QuerySet, set): Left-side QuerySet, or set of values derived from a queryset by get_prefixes()
504
+ b (QuerySet, set): Right-side QuerySet, or set of values derived from a queryset by get_prefixes()
466
505
 
467
506
  Returns:
468
- set(tuple)
507
+ bool
469
508
  """
470
- set_a = set(get_prefixes(a))
471
- set_b = set(get_prefixes(b))
472
- return set_a.intersection(set_b)
509
+ set_a = get_prefixes(a)
510
+ set_b = get_prefixes(b)
511
+ return bool(set_a.intersection(set_b))
473
512
 
474
513
 
475
514
  def create_vrf_namespace(apps, vrf):
@@ -528,11 +567,11 @@ def generate_parent_prefix(apps, address):
528
567
  Returns:
529
568
  netaddr.IPNetwork
530
569
  """
531
- cidr = validate_cidr(apps, address)
570
+ cidr = validate_cidr(address)
532
571
  return cidr.cidr
533
572
 
534
573
 
535
- def get_closest_parent(apps, obj, qs):
574
+ def get_closest_parent(obj, qs):
536
575
  """
537
576
  This is forklifted from `Prefix.objects.get_closest_parent()` so that it can safely be used in
538
577
  migrations.
@@ -540,19 +579,16 @@ def get_closest_parent(apps, obj, qs):
540
579
  Return the closest matching parent Prefix for a `cidr` even if it doesn't exist in the database.
541
580
 
542
581
  Args:
543
- obj: Prefix/IPAddress instance
582
+ obj (IPAddress, Prefix): Prefix/IPAddress instance
544
583
  qs (QuerySet): QuerySet of Prefix objects
545
584
 
546
585
  Returns:
547
- Prefix or a filtered queryset
586
+ Prefix or None
548
587
  """
549
588
  # Validate that it's a real CIDR
550
- cidr = validate_cidr(apps, obj)
589
+ cidr = validate_cidr(obj)
551
590
  broadcast = str(cidr.broadcast or cidr.ip)
552
591
 
553
- Prefix = apps.get_model("ipam", "Prefix")
554
- IPAddress = apps.get_model("ipam", "IPAddress")
555
-
556
592
  # Prepare the queryset filter
557
593
  lookup_kwargs = {
558
594
  "ip_version": cidr.version,
@@ -560,9 +596,8 @@ def get_closest_parent(apps, obj, qs):
560
596
  "broadcast__gte": broadcast,
561
597
  }
562
598
 
563
- if isinstance(obj, Prefix):
599
+ if is_prefix(obj):
564
600
  lookup_kwargs["prefix_length__lt"] = cidr.prefixlen
565
- qs = qs.exclude(id=obj.id)
566
601
  else:
567
602
  lookup_kwargs["prefix_length__lte"] = cidr.prefixlen
568
603
 
@@ -572,10 +607,10 @@ def get_closest_parent(apps, obj, qs):
572
607
  qs.filter(**lookup_kwargs)
573
608
  .annotate(
574
609
  custom_sort_order=models.Case(
575
- models.When(tenant=obj.tenant, vrf=obj.vrf, then=models.Value(1)),
576
- models.When(tenant__isnull=True, vrf=obj.vrf, then=models.Value(2)),
577
- models.When(tenant=obj.tenant, vrf__isnull=True, then=models.Value(3)),
578
- models.When(vrf=obj.vrf, then=models.Value(4)),
610
+ models.When(tenant_id=obj.tenant_id, vrf_id=obj.vrf_id, then=models.Value(1)),
611
+ models.When(tenant__isnull=True, vrf_id=obj.vrf_id, then=models.Value(2)),
612
+ models.When(tenant_id=obj.tenant_id, vrf__isnull=True, then=models.Value(3)),
613
+ models.When(vrf_id=obj.vrf_id, then=models.Value(4)),
579
614
  models.When(tenant__isnull=True, vrf__isnull=True, then=models.Value(5)),
580
615
  models.When(vrf__isnull=True, then=models.Value(6)),
581
616
  default=models.Value(7),
@@ -584,21 +619,19 @@ def get_closest_parent(apps, obj, qs):
584
619
  .order_by("-prefix_length", "custom_sort_order")
585
620
  )
586
621
 
587
- if isinstance(obj, IPAddress):
622
+ if is_ipaddress(obj):
588
623
  # IP should not fall back to less specific prefixes
589
- if not possible_ancestors.exists():
590
- return qs.none()
591
- prefix_length = possible_ancestors.first().prefix_length
592
- return possible_ancestors.filter(prefix_length=prefix_length)
624
+ first_ancestor = possible_ancestors.only("prefix_length").first()
625
+ if not first_ancestor:
626
+ return None
627
+ prefix_length = first_ancestor.prefix_length
628
+ possible_ancestors = possible_ancestors.filter(prefix_length=prefix_length).exclude(ip_addresses__host=obj.host)
593
629
 
594
630
  # If we've got any matches, the first one is our closest parent.
595
- try:
596
- return possible_ancestors[0]
597
- except IndexError:
598
- raise Prefix.DoesNotExist(f"Could not determine parent Prefix for {cidr}")
631
+ return possible_ancestors.first()
599
632
 
600
633
 
601
- def get_next_vrf_cleanup_namespace(apps, vrf):
634
+ def get_next_vrf_cleanup_namespace(apps, vrf, global_ns):
602
635
  """
603
636
  Try to get the next available Cleanup Namespace based on `vrf` found in the "Global" Namespace.
604
637
 
@@ -610,6 +643,7 @@ def get_next_vrf_cleanup_namespace(apps, vrf):
610
643
  Args:
611
644
  apps: Django apps module
612
645
  vrf (VRF): VRF instance
646
+ global_ns (Namespace): Global Namespace.
613
647
 
614
648
  Returns:
615
649
  Namespace
@@ -618,21 +652,22 @@ def get_next_vrf_cleanup_namespace(apps, vrf):
618
652
  VRF = apps.get_model("ipam", "VRF")
619
653
 
620
654
  counter = 1
621
- vrf_prefixes = vrf.prefixes.all()
655
+ vrf_prefixes = get_prefixes(vrf.prefixes.all())
622
656
 
623
- global_ns = Namespace.objects.get(name=GLOBAL_NS)
624
- global_ns_prefixes = global_ns.prefixes.exclude(vrf=vrf)
625
- global_dupe_prefixes = compare_prefix_querysets(vrf_prefixes, global_ns_prefixes)
626
657
  global_dupe_vrfs = VRF.objects.filter(namespace=global_ns, name=vrf.name).exclude(pk=vrf.pk).exists()
627
658
 
628
- if global_dupe_prefixes and "test" not in sys.argv:
629
- print(f" VRF {vrf.name} has duplicate prefixes with NS {global_ns.name}")
630
-
631
- if global_dupe_vrfs and "test" not in sys.argv:
632
- print(f" VRF {vrf.name} has duplicate VRF name with NS {global_ns.name}")
633
-
634
- if not any([global_dupe_prefixes, global_dupe_vrfs]):
635
- return global_ns
659
+ if global_dupe_vrfs:
660
+ if "test" not in sys.argv:
661
+ print(f" VRF {vrf.name} has duplicate VRF name with NS {global_ns.name}")
662
+ else:
663
+ global_ns_prefixes = global_ns.prefixes.exclude(vrf=vrf)
664
+ global_dupe_prefixes = compare_prefix_querysets(vrf_prefixes, global_ns_prefixes)
665
+ if global_dupe_prefixes:
666
+ if "test" not in sys.argv:
667
+ print(f" VRF {vrf.name} has duplicate prefixes with NS {global_ns.name}")
668
+ else:
669
+ # No duplicate VRF or duplicate prefixes - just stay in global Namespace
670
+ return global_ns
636
671
 
637
672
  # Iterate non-enforce_unique VRFS
638
673
  # - Compare duplicate prefixes for each VRF
@@ -646,17 +681,18 @@ def get_next_vrf_cleanup_namespace(apps, vrf):
646
681
  if created:
647
682
  return namespace
648
683
 
649
- ns_prefixes = namespace.prefixes.exclude(vrf=vrf)
650
- dupe_prefixes = compare_prefix_querysets(vrf_prefixes, ns_prefixes)
651
684
  dupe_vrfs = VRF.objects.filter(namespace=namespace, name=vrf.name).exclude(pk=vrf.pk).exists()
685
+ if dupe_vrfs:
686
+ if "test" not in sys.argv:
687
+ print(f" VRF {vrf.name} has duplicate VRF name with NS {namespace.name}")
688
+ counter += 1
689
+ continue
652
690
 
653
- if dupe_prefixes and "test" not in sys.argv:
654
- print(f" VRF {vrf.name} has duplicate prefixes with NS {namespace.name}")
655
-
656
- if dupe_vrfs and "test" not in sys.argv:
657
- print(f" VRF {vrf.name} has duplicate VRF name with NS {namespace.name}")
658
-
659
- if any([dupe_prefixes, dupe_vrfs]):
691
+ ns_prefixes = namespace.prefixes.exclude(vrf=vrf)
692
+ dupe_prefixes = compare_prefix_querysets(vrf_prefixes, ns_prefixes)
693
+ if dupe_prefixes:
694
+ if "test" not in sys.argv:
695
+ print(f" VRF {vrf.name} has duplicate prefixes with NS {namespace.name}")
660
696
  counter += 1
661
697
  continue
662
698
 
@@ -704,7 +740,7 @@ def get_next_prefix_cleanup_namespace(apps, prefix, base_name=BASE_NAME):
704
740
  return namespace
705
741
 
706
742
 
707
- def validate_cidr(apps, value):
743
+ def validate_cidr(value):
708
744
  """
709
745
  Validate whether `value` is a valid IPv4/IPv6 CIDR.
710
746
 
@@ -714,12 +750,9 @@ def validate_cidr(apps, value):
714
750
  Returns:
715
751
  netaddr.IPNetwork
716
752
  """
717
- IPAddress = apps.get_model("ipam", "IPAddress")
718
- Prefix = apps.get_model("ipam", "Prefix")
719
-
720
- if isinstance(value, IPAddress):
753
+ if is_ipaddress(value):
721
754
  value = f"{value.host}/{value.prefix_length}"
722
- elif isinstance(value, Prefix):
755
+ elif is_prefix(value):
723
756
  value = f"{value.network}/{value.prefix_length}"
724
757
  else:
725
758
  value = str(value)
@@ -742,7 +775,7 @@ def ensure_correct_prefix_broadcast(apps):
742
775
  """
743
776
  Prefix = apps.get_model("ipam", "Prefix")
744
777
 
745
- for prefix in Prefix.objects.all():
778
+ for prefix in Prefix.objects.all().iterator():
746
779
  true_broadcast = str(netaddr.IPNetwork(f"{prefix.network}/{prefix.prefix_length}")[-1])
747
780
  if prefix.broadcast != true_broadcast:
748
781
  if "test" not in sys.argv: