nautobot 2.2.4__py3-none-any.whl → 2.2.6__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 (365) hide show
  1. nautobot/apps/api.py +2 -0
  2. nautobot/apps/models.py +2 -0
  3. nautobot/circuits/forms.py +15 -0
  4. nautobot/circuits/navigation.py +9 -1
  5. nautobot/circuits/views.py +2 -0
  6. nautobot/core/api/fields.py +13 -0
  7. nautobot/core/api/serializers.py +7 -1
  8. nautobot/core/filters.py +11 -0
  9. nautobot/core/management/commands/generate_test_data.py +128 -158
  10. nautobot/core/models/fields.py +15 -0
  11. nautobot/core/settings.yaml +3 -3
  12. nautobot/core/testing/filters.py +24 -1
  13. nautobot/core/testing/views.py +13 -1
  14. nautobot/core/tests/test_utils.py +48 -1
  15. nautobot/core/utils/git.py +121 -49
  16. nautobot/core/utils/module_loading.py +10 -2
  17. nautobot/core/views/utils.py +18 -1
  18. nautobot/dcim/factory.py +1 -1
  19. nautobot/dcim/filters/__init__.py +1 -1
  20. nautobot/dcim/forms.py +23 -4
  21. nautobot/dcim/tables/devicetypes.py +15 -4
  22. nautobot/dcim/tests/test_models.py +2 -0
  23. nautobot/dcim/tests/test_views.py +84 -0
  24. nautobot/dcim/views.py +3 -0
  25. nautobot/extras/api/views.py +2 -2
  26. nautobot/extras/context_managers.py +3 -0
  27. nautobot/extras/datasources/git.py +133 -135
  28. nautobot/extras/datasources/utils.py +3 -0
  29. nautobot/extras/filters/__init__.py +16 -1
  30. nautobot/extras/forms/forms.py +49 -3
  31. nautobot/extras/forms/mixins.py +0 -6
  32. nautobot/extras/jobs.py +9 -1
  33. nautobot/extras/migrations/0107_laxurlfield.py +28 -0
  34. nautobot/extras/migrations/0108_jobbutton_enabled.py +17 -0
  35. nautobot/extras/models/datasources.py +6 -4
  36. nautobot/extras/models/jobs.py +30 -0
  37. nautobot/extras/models/models.py +2 -4
  38. nautobot/extras/signals.py +6 -1
  39. nautobot/extras/tables.py +3 -0
  40. nautobot/extras/templates/extras/jobbutton_retrieve.html +6 -2
  41. nautobot/extras/templatetags/job_buttons.py +2 -2
  42. nautobot/extras/tests/git_data/01-valid-files/__init__.py +0 -0
  43. nautobot/extras/tests/git_data/01-valid-files/config_context_schemas/schema-1.yaml +18 -0
  44. nautobot/extras/tests/git_data/01-valid-files/config_contexts/context.yaml +12 -0
  45. nautobot/extras/tests/git_data/01-valid-files/config_contexts/devices/test-device.json +3 -0
  46. nautobot/extras/tests/git_data/01-valid-files/config_contexts/locations/Test Location.json +7 -0
  47. nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template.j2 +3 -0
  48. nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template2.html +4 -0
  49. nautobot/extras/tests/git_data/01-valid-files/export_templates/ipam/vlan/template.j2 +3 -0
  50. nautobot/extras/tests/git_data/01-valid-files/jobs/__init__.py +5 -0
  51. nautobot/extras/tests/git_data/01-valid-files/jobs/my_job.py +16 -0
  52. nautobot/extras/tests/git_data/02-invalid-files/__init__.py +0 -0
  53. nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema1.json +2 -0
  54. nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema2.json +1 -0
  55. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext1.json +2 -0
  56. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext2.json +1 -0
  57. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext3.json +3 -0
  58. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/devices/nosuchdevice.json +1 -0
  59. nautobot/extras/tests/git_data/02-invalid-files/dcim/template.j2 +0 -0
  60. nautobot/extras/tests/git_data/02-invalid-files/devices/template.j2 +0 -0
  61. nautobot/extras/tests/git_data/02-invalid-files/export_templates/dcim/nosuchmodel/template.j2 +3 -0
  62. nautobot/extras/tests/git_data/02-invalid-files/export_templates/nosuchapp/device/template.j2 +3 -0
  63. nautobot/extras/tests/git_data/02-invalid-files/jobs/__init__.py +2 -0
  64. nautobot/extras/tests/git_data/02-invalid-files/jobs/importerror.py +1 -0
  65. nautobot/extras/tests/git_data/02-invalid-files/jobs/syntaxerror.py +1 -0
  66. nautobot/extras/tests/git_helper.py +76 -0
  67. nautobot/extras/tests/test_api.py +52 -13
  68. nautobot/extras/tests/test_context_managers.py +33 -1
  69. nautobot/extras/tests/test_datasources.py +94 -276
  70. nautobot/extras/tests/test_filters.py +69 -0
  71. nautobot/extras/tests/test_forms.py +0 -3
  72. nautobot/extras/tests/test_models.py +8 -3
  73. nautobot/extras/tests/test_views.py +69 -11
  74. nautobot/extras/views.py +12 -10
  75. nautobot/ipam/filters.py +9 -1
  76. nautobot/ipam/forms.py +26 -0
  77. nautobot/ipam/tables.py +1 -1
  78. nautobot/ipam/tests/test_filters.py +15 -0
  79. nautobot/ipam/tests/test_views.py +9 -2
  80. nautobot/ipam/views.py +11 -0
  81. nautobot/project-static/docs/404.html +84 -9
  82. nautobot/project-static/docs/apps/index.html +97 -11
  83. nautobot/project-static/docs/apps/nautobot-apps.html +97 -11
  84. nautobot/project-static/docs/assets/app-icons/icon-CapacityMetrics.svg +1 -0
  85. nautobot/project-static/docs/assets/app-icons/icon-CircuitMaintenance.png +0 -0
  86. nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js → bundle.ad660dcc.min.js} +6 -6
  87. nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js.map → bundle.ad660dcc.min.js.map} +3 -3
  88. nautobot/project-static/docs/assets/javascripts/glightbox.min.js +1 -0
  89. nautobot/project-static/docs/assets/stylesheets/glightbox.min.css +1 -0
  90. nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css → main.6543a935.min.css} +1 -1
  91. nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css.map → main.6543a935.min.css.map} +1 -1
  92. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +97 -11
  93. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +97 -11
  94. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +97 -11
  95. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +97 -11
  96. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +97 -11
  97. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +97 -11
  98. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +97 -11
  99. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +97 -11
  100. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +97 -11
  101. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +97 -11
  102. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +97 -11
  103. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +97 -11
  104. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +97 -11
  105. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +97 -11
  106. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +157 -13
  107. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +97 -11
  108. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +97 -11
  109. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +97 -11
  110. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -12
  111. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +97 -11
  112. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +97 -11
  113. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +144 -12
  114. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +97 -11
  115. nautobot/project-static/docs/development/apps/api/configuration-view.html +97 -11
  116. nautobot/project-static/docs/development/apps/api/database-backend-config.html +97 -11
  117. nautobot/project-static/docs/development/apps/api/models/django-admin.html +97 -11
  118. nautobot/project-static/docs/development/apps/api/models/global-search.html +97 -11
  119. nautobot/project-static/docs/development/apps/api/models/graphql.html +97 -11
  120. nautobot/project-static/docs/development/apps/api/models/index.html +97 -11
  121. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +97 -11
  122. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +97 -11
  123. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +98 -12
  124. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +97 -11
  125. nautobot/project-static/docs/development/apps/api/platform-features/index.html +97 -11
  126. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +97 -11
  127. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +97 -11
  128. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +97 -11
  129. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +97 -11
  130. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +97 -11
  131. nautobot/project-static/docs/development/apps/api/prometheus.html +97 -11
  132. nautobot/project-static/docs/development/apps/api/setup.html +97 -11
  133. nautobot/project-static/docs/development/apps/api/testing.html +97 -11
  134. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +97 -11
  135. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +97 -11
  136. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +97 -11
  137. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +97 -11
  138. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +97 -11
  139. nautobot/project-static/docs/development/apps/api/views/base-template.html +97 -11
  140. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +97 -11
  141. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +97 -11
  142. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +97 -11
  143. nautobot/project-static/docs/development/apps/api/views/index.html +97 -11
  144. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +97 -11
  145. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +97 -11
  146. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +97 -11
  147. nautobot/project-static/docs/development/apps/api/views/notes.html +97 -11
  148. nautobot/project-static/docs/development/apps/api/views/rest-api.html +97 -11
  149. nautobot/project-static/docs/development/apps/api/views/urls.html +97 -11
  150. nautobot/project-static/docs/development/apps/index.html +97 -11
  151. nautobot/project-static/docs/development/apps/migration/code-updates.html +98 -12
  152. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +97 -11
  153. nautobot/project-static/docs/development/apps/migration/from-v1.html +99 -13
  154. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +97 -11
  155. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +97 -11
  156. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +97 -11
  157. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +97 -11
  158. nautobot/project-static/docs/development/apps/porting-from-netbox.html +97 -11
  159. nautobot/project-static/docs/development/core/application-registry.html +98 -12
  160. nautobot/project-static/docs/development/core/best-practices.html +97 -11
  161. nautobot/project-static/docs/development/core/bootstrap-ui.html +97 -11
  162. nautobot/project-static/docs/development/core/caching.html +97 -11
  163. nautobot/project-static/docs/development/core/controllers.html +97 -11
  164. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +97 -11
  165. nautobot/project-static/docs/development/core/generic-views.html +97 -11
  166. nautobot/project-static/docs/development/core/getting-started.html +99 -13
  167. nautobot/project-static/docs/development/core/homepage.html +97 -11
  168. nautobot/project-static/docs/development/core/index.html +97 -11
  169. nautobot/project-static/docs/development/core/model-checklist.html +97 -11
  170. nautobot/project-static/docs/development/core/model-features.html +97 -11
  171. nautobot/project-static/docs/development/core/natural-keys.html +97 -11
  172. nautobot/project-static/docs/development/core/navigation-menu.html +97 -11
  173. nautobot/project-static/docs/development/core/release-checklist.html +97 -11
  174. nautobot/project-static/docs/development/core/role-internals.html +97 -11
  175. nautobot/project-static/docs/development/core/settings.html +97 -11
  176. nautobot/project-static/docs/development/core/style-guide.html +97 -11
  177. nautobot/project-static/docs/development/core/templates.html +97 -11
  178. nautobot/project-static/docs/development/core/testing.html +98 -12
  179. nautobot/project-static/docs/development/core/user-preferences.html +97 -11
  180. nautobot/project-static/docs/development/index.html +97 -11
  181. nautobot/project-static/docs/development/jobs/index.html +97 -11
  182. nautobot/project-static/docs/development/jobs/migration/from-v1.html +97 -11
  183. nautobot/project-static/docs/index.html +13 -8362
  184. nautobot/project-static/docs/objects.inv +0 -0
  185. nautobot/project-static/docs/overview/application_stack.html +8229 -0
  186. nautobot/project-static/docs/overview/design_philosophy.html +8158 -0
  187. nautobot/project-static/docs/overview/index.html +8230 -0
  188. nautobot/project-static/docs/release-notes/index.html +97 -11
  189. nautobot/project-static/docs/release-notes/version-1.0.html +98 -12
  190. nautobot/project-static/docs/release-notes/version-1.1.html +97 -11
  191. nautobot/project-static/docs/release-notes/version-1.2.html +99 -13
  192. nautobot/project-static/docs/release-notes/version-1.3.html +98 -12
  193. nautobot/project-static/docs/release-notes/version-1.4.html +99 -13
  194. nautobot/project-static/docs/release-notes/version-1.5.html +99 -13
  195. nautobot/project-static/docs/release-notes/version-1.6.html +626 -160
  196. nautobot/project-static/docs/release-notes/version-2.0.html +100 -14
  197. nautobot/project-static/docs/release-notes/version-2.1.html +97 -11
  198. nautobot/project-static/docs/release-notes/version-2.2.html +546 -107
  199. nautobot/project-static/docs/requirements.txt +3 -2
  200. nautobot/project-static/docs/search/search_index.json +1 -1
  201. nautobot/project-static/docs/sitemap.xml +268 -258
  202. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  203. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +97 -11
  204. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +97 -11
  205. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +99 -13
  206. nautobot/project-static/docs/user-guide/administration/configuration/index.html +98 -12
  207. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +100 -14
  208. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +97 -11
  209. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +97 -11
  210. nautobot/project-static/docs/user-guide/administration/guides/caching.html +97 -11
  211. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +97 -11
  212. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +97 -11
  213. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +97 -11
  214. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +97 -11
  215. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +98 -12
  216. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +97 -11
  217. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +97 -11
  218. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +97 -11
  219. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +97 -11
  220. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +97 -11
  221. nautobot/project-static/docs/user-guide/administration/installation/index.html +97 -11
  222. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +97 -11
  223. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +101 -15
  224. nautobot/project-static/docs/user-guide/administration/installation/services.html +98 -12
  225. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +97 -11
  226. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +97 -11
  227. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +97 -11
  228. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +97 -11
  229. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +97 -11
  230. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +97 -11
  231. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +97 -11
  232. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +97 -11
  233. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +97 -11
  234. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +97 -11
  235. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +98 -12
  236. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +97 -11
  237. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +97 -11
  238. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +97 -30
  239. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-location-changes.yaml +1 -1
  240. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +100 -14
  241. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +97 -11
  242. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +97 -11
  243. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +97 -11
  244. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +97 -11
  245. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +97 -11
  246. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +97 -11
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +97 -11
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +97 -11
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +97 -11
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +97 -11
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +97 -11
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +97 -11
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +97 -11
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +99 -13
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +97 -11
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +97 -11
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +97 -11
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +97 -11
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +98 -12
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +97 -11
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +97 -11
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +97 -11
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +97 -11
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +97 -11
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +97 -11
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +97 -11
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +97 -11
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +97 -11
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +97 -11
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +97 -11
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +97 -11
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +97 -11
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +97 -11
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +97 -11
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +97 -11
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +97 -11
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +97 -11
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +97 -11
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +97 -11
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +97 -11
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +97 -11
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +97 -11
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +97 -11
  284. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +97 -11
  285. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +97 -11
  286. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +97 -11
  287. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +97 -11
  288. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +97 -11
  289. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +97 -11
  290. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +97 -11
  291. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +97 -11
  292. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +97 -11
  293. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +97 -11
  294. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +97 -11
  295. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +97 -11
  296. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +97 -11
  297. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +97 -11
  298. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +97 -11
  299. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +97 -11
  300. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +97 -11
  301. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +97 -11
  302. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +97 -11
  303. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +97 -11
  304. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +97 -11
  305. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +97 -11
  306. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +97 -11
  307. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +100 -14
  308. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +97 -11
  309. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +97 -11
  310. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +98 -12
  311. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +97 -11
  312. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +97 -11
  313. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +97 -11
  314. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +97 -11
  315. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +98 -12
  316. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +97 -11
  317. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +97 -11
  318. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +97 -11
  319. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +97 -11
  320. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +97 -11
  321. nautobot/project-static/docs/user-guide/index.html +100 -14
  322. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +97 -11
  323. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +97 -11
  324. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +97 -11
  325. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +97 -11
  326. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +97 -11
  327. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +97 -11
  328. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +97 -11
  329. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +97 -11
  330. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +97 -11
  331. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +97 -11
  332. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +97 -11
  333. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +98 -12
  334. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +98 -12
  335. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +102 -15
  336. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +97 -11
  337. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +99 -13
  338. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +99 -13
  339. nautobot/project-static/docs/user-guide/platform-functionality/note.html +97 -11
  340. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +97 -11
  341. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +97 -11
  342. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +97 -11
  343. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +97 -11
  344. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +97 -11
  345. nautobot/project-static/docs/user-guide/platform-functionality/role.html +98 -44
  346. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +97 -11
  347. nautobot/project-static/docs/user-guide/platform-functionality/status.html +97 -11
  348. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +97 -11
  349. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +97 -11
  350. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +97 -11
  351. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +97 -11
  352. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +97 -11
  353. nautobot/project-static/js/forms.js +2 -1
  354. nautobot/tenancy/forms.py +9 -0
  355. nautobot/tenancy/views.py +1 -0
  356. nautobot/virtualization/forms.py +18 -6
  357. nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
  358. nautobot/virtualization/views.py +2 -0
  359. {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/METADATA +1 -1
  360. {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/RECORD +364 -331
  361. nautobot/extras/tests/test_git.py +0 -23
  362. {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/LICENSE.txt +0 -0
  363. {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/NOTICE +0 -0
  364. {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/WHEEL +0 -0
  365. {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/entry_points.txt +0 -0
nautobot/apps/api.py CHANGED
@@ -12,6 +12,7 @@ from nautobot.core.api import (
12
12
  from nautobot.core.api.fields import (
13
13
  ChoiceField,
14
14
  ContentTypeField,
15
+ LaxURLField,
15
16
  NautobotHyperlinkedRelatedField,
16
17
  ObjectTypeField,
17
18
  SerializedPKRelatedField,
@@ -63,6 +64,7 @@ __all__ = (
63
64
  "get_view_name",
64
65
  "GetObjectCountsView",
65
66
  "is_api_request",
67
+ "LaxURLField",
66
68
  "ModelViewSet",
67
69
  "ModelViewSetMixin",
68
70
  "MultipleChoiceJSONField",
nautobot/apps/models.py CHANGED
@@ -8,6 +8,7 @@ from nautobot.core.models.fields import (
8
8
  ForeignKeyLimitedByContentTypes,
9
9
  ForeignKeyWithAutoRelatedName,
10
10
  JSONArrayField,
11
+ LaxURLField,
11
12
  mac_unix_expanded_uppercase,
12
13
  MACAddressCharField,
13
14
  NaturalOrderingField,
@@ -83,6 +84,7 @@ __all__ = (
83
84
  "is_taggable",
84
85
  "JSONArrayField",
85
86
  "JSONBAgg",
87
+ "LaxURLField",
86
88
  "mac_unix_expanded_uppercase",
87
89
  "MACAddressCharField",
88
90
  "NameColorContentTypesModel",
@@ -145,6 +145,12 @@ class CircuitTypeForm(NautobotModelForm):
145
145
  ]
146
146
 
147
147
 
148
+ class CircuitTypeFilterForm(NautobotFilterForm):
149
+ model = CircuitType
150
+ q = forms.CharField(required=False, label="Search")
151
+ name = forms.CharField(required=False)
152
+
153
+
148
154
  #
149
155
  # Circuits
150
156
  #
@@ -262,3 +268,12 @@ class CircuitTerminationForm(LocatableModelFormMixin, NautobotModelForm):
262
268
  widgets = {
263
269
  "term_side": forms.HiddenInput(),
264
270
  }
271
+
272
+
273
+ class CircuitTerminationFilterForm(LocatableModelFilterFormMixin, NautobotFilterForm):
274
+ model = CircuitTermination
275
+ q = forms.CharField(required=False, label="Search")
276
+ circuit = DynamicModelMultipleChoiceField(queryset=Circuit.objects.all(), to_field_name="cid", required=False)
277
+ provider_network = DynamicModelMultipleChoiceField(
278
+ queryset=ProviderNetwork.objects.all(), to_field_name="name", required=False
279
+ )
@@ -33,10 +33,18 @@ menu_items = (
33
33
  ),
34
34
  ),
35
35
  ),
36
+ NavMenuItem(
37
+ link="circuits:circuittermination_list",
38
+ name="Circuit Terminations",
39
+ weight=200,
40
+ permissions=[
41
+ "circuits.view_circuittermination",
42
+ ],
43
+ ),
36
44
  NavMenuItem(
37
45
  link="circuits:circuittype_list",
38
46
  name="Circuit Types",
39
- weight=200,
47
+ weight=300,
40
48
  permissions=[
41
49
  "circuits.view_circuittype",
42
50
  ],
@@ -27,6 +27,7 @@ class CircuitTypeUIViewSet(
27
27
  view_mixins.ObjectNotesViewMixin,
28
28
  ):
29
29
  filterset_class = filters.CircuitTypeFilterSet
30
+ filterset_form_class = forms.CircuitTypeFilterForm
30
31
  form_class = forms.CircuitTypeForm
31
32
  queryset = CircuitType.objects.annotate(circuit_count=count_related(Circuit, "circuit_type"))
32
33
  serializer_class = serializers.CircuitTypeSerializer
@@ -67,6 +68,7 @@ class CircuitTerminationUIViewSet(
67
68
  ):
68
69
  action_buttons = ("import", "export")
69
70
  filterset_class = filters.CircuitTerminationFilterSet
71
+ filterset_form_class = forms.CircuitTerminationFilterForm
70
72
  form_class = forms.CircuitTerminationForm
71
73
  queryset = CircuitTermination.objects.all()
72
74
  serializer_class = serializers.CircuitTerminationSerializer
@@ -2,15 +2,18 @@ from collections import OrderedDict
2
2
  import logging
3
3
 
4
4
  from django.core.exceptions import ObjectDoesNotExist
5
+ from django.core.validators import URLValidator
5
6
  from django.db.models import Model
6
7
  from drf_spectacular.utils import extend_schema_field
7
8
  from rest_framework import serializers
8
9
  from rest_framework.exceptions import ValidationError
10
+ from rest_framework.fields import URLField
9
11
  from rest_framework.relations import PrimaryKeyRelatedField, RelatedField
10
12
  from timezone_field.rest_framework import TimeZoneSerializerField as TimeZoneSerializerField_
11
13
 
12
14
  from nautobot.core.api.mixins import WritableSerializerMixin
13
15
  from nautobot.core.models.utils import deconstruct_composite_key
16
+ from nautobot.core.models.validators import EnhancedURLValidator
14
17
  from nautobot.core.utils.data import is_url, is_uuid
15
18
  from nautobot.core.utils.lookup import get_route_for_model
16
19
 
@@ -124,6 +127,16 @@ class ContentTypeField(RelatedField):
124
127
  return f"{obj.app_label}.{obj.model}"
125
128
 
126
129
 
130
+ class LaxURLField(URLField):
131
+ def __init__(self, validators=None, **kwargs):
132
+ super().__init__(**kwargs)
133
+ # Discard default URLValidator added by URLField
134
+ self.validators = [v for v in self.validators if not isinstance(v, URLValidator)]
135
+ if validators is not None:
136
+ self.validators.extend(validators)
137
+ self.validators.append(EnhancedURLValidator(message=self.error_messages["invalid"]))
138
+
139
+
127
140
  @extend_schema_field(
128
141
  {
129
142
  "type": "object",
@@ -26,12 +26,13 @@ from rest_framework.serializers import SerializerMethodField
26
26
  from rest_framework.utils.model_meta import _get_to_field, RelationInfo
27
27
 
28
28
  from nautobot.core import constants
29
- from nautobot.core.api.fields import NautobotHyperlinkedRelatedField, ObjectTypeField
29
+ from nautobot.core.api.fields import LaxURLField, NautobotHyperlinkedRelatedField, ObjectTypeField
30
30
  from nautobot.core.api.utils import (
31
31
  dict_to_filter_params,
32
32
  nested_serializer_factory,
33
33
  )
34
34
  from nautobot.core.exceptions import ViewConfigException
35
+ from nautobot.core.models.fields import LaxURLField as LaxURLModelField
35
36
  from nautobot.core.models.managers import TagsManager
36
37
  from nautobot.core.models.utils import construct_composite_key, construct_natural_slug
37
38
  from nautobot.core.templatetags.helpers import bettertitle
@@ -126,6 +127,11 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
126
127
  to enable the dynamic generation of nested serializers.
127
128
  """
128
129
 
130
+ serializer_field_mapping = {
131
+ **serializers.ModelSerializer.serializer_field_mapping,
132
+ LaxURLModelField: LaxURLField,
133
+ }
134
+
129
135
  serializer_related_field = NautobotHyperlinkedRelatedField
130
136
 
131
137
  display = serializers.SerializerMethodField(read_only=True, help_text="Human friendly display value")
nautobot/core/filters.py CHANGED
@@ -176,6 +176,9 @@ class ContentTypeFilterMixin:
176
176
  if value in EMPTY_VALUES:
177
177
  return qs
178
178
 
179
+ if value.isdigit():
180
+ return qs.filter(**{f"{self.field_name}__pk": value})
181
+
179
182
  try:
180
183
  app_label, model = value.lower().split(".")
181
184
  except ValueError:
@@ -242,6 +245,9 @@ class ContentTypeMultipleChoiceFilter(django_filters.MultipleChoiceFilter):
242
245
  if self.conjoined:
243
246
  qs = ContentTypeFilter.filter(self, qs, v)
244
247
  else:
248
+ if v.isdigit():
249
+ q |= models.Q(**{f"{self.field_name}__pk": value})
250
+ continue
245
251
  # Similar to the ContentTypeFilter.filter() call above, but instead of narrowing the query each time
246
252
  # (a AND b AND c ...) we broaden the query each time (a OR b OR c ...).
247
253
  # Specifically, we're mapping a value like ['dcim.device', 'ipam.vlan'] to a query like
@@ -716,6 +722,11 @@ class BaseFilterSet(django_filters.FilterSet):
716
722
  """
717
723
  filters = super().get_filters()
718
724
 
725
+ # Remove any filters that may have been auto-generated from private model attributes
726
+ for filter_name in list(filters.keys()):
727
+ if filter_name.startswith("_"):
728
+ del filters[filter_name]
729
+
719
730
  # django-filters has no concept of "abstract" filtersets, so we have to fake it
720
731
  if cls._meta.model is not None:
721
732
  new_filters = {}
@@ -30,6 +30,17 @@ class Command(BaseCommand):
30
30
  dest="interactive",
31
31
  help="Do NOT prompt the user for input or confirmation of any kind.",
32
32
  )
33
+ parser.add_argument(
34
+ "--print-hashes",
35
+ action="store_true",
36
+ help=(
37
+ "After creating each batch of records, print a hash of the list of all IDs of all objects of "
38
+ "the given type. This is useful for identifying any problems with factory randomness / determinism; "
39
+ "in general, successive runs with the same seed should output identical hashes for each stage, "
40
+ "while successive runs with differing seeds should output different hashes. "
41
+ "Setting environment variable GITHUB_ACTIONS to true is equivalent to specifying this argument."
42
+ ),
43
+ )
33
44
  parser.add_argument(
34
45
  "--cache-test-fixtures",
35
46
  action="store_true",
@@ -46,7 +57,7 @@ class Command(BaseCommand):
46
57
  help='The database to generate the test data in. Defaults to the "default" database.',
47
58
  )
48
59
 
49
- def _generate_factory_data(self, seed, db_name):
60
+ def _generate_factory_data(self, seed, db_name, print_hashes=False):
50
61
  try:
51
62
  import factory.random
52
63
 
@@ -83,7 +94,6 @@ class Command(BaseCommand):
83
94
  from nautobot.extras.utils import TaggableClassesQuery
84
95
  from nautobot.ipam.choices import PrefixTypeChoices
85
96
  from nautobot.ipam.factory import (
86
- IPAddressFactory,
87
97
  NamespaceFactory,
88
98
  PrefixFactory,
89
99
  RIRFactory,
@@ -101,178 +111,135 @@ class Command(BaseCommand):
101
111
  self.stdout.write(f'Seeding the pseudo-random number generator with seed "{seed}"...')
102
112
  factory.random.reseed_random(seed)
103
113
 
104
- self.stdout.write("Creating Roles...")
114
+ def _create_batch(some_factory, count, description="", **kwargs):
115
+ model = some_factory._meta.get_model_class()
116
+ if description:
117
+ description = " " + description
118
+ message = f"Creating {count} {model._meta.verbose_name_plural}{description}..."
119
+ self.stdout.write(message)
120
+ records = some_factory.create_batch(count, using=db_name, **kwargs)
121
+ if print_hashes:
122
+ model_ids = [record.id for record in records]
123
+ sha256_hash = hashlib.sha256(json.dumps(model_ids, cls=DjangoJSONEncoder).encode()).hexdigest()
124
+ self.stdout.write(f" SHA256: {sha256_hash}")
125
+
105
126
  populate_role_choices(verbosity=0, using=db_name)
106
- RoleFactory.create_batch(20)
107
- self.stdout.write("Creating Statuses...")
127
+ _create_batch(RoleFactory, 20)
108
128
  populate_status_choices(verbosity=0, using=db_name)
109
- StatusFactory.create_batch(10, using=db_name)
110
- self.stdout.write("Creating Tags...")
129
+ _create_batch(StatusFactory, 10)
111
130
  # Ensure that we have some tags that are applicable to all relevant content-types
112
- TagFactory.create_batch(5, content_types=TaggableClassesQuery().as_queryset(), using=db_name)
131
+ _create_batch(
132
+ TagFactory, 5, description="on all content-types", content_types=TaggableClassesQuery().as_queryset()
133
+ )
113
134
  # ...and some tags that apply to a random subset of content-types
114
- TagFactory.create_batch(15, using=db_name)
115
- self.stdout.write("Creating Contacts...")
116
- ContactFactory.create_batch(20, using=db_name)
117
- self.stdout.write("Creating Teams...")
118
- TeamFactory.create_batch(20, using=db_name)
119
- self.stdout.write("Creating TenantGroups...")
120
- TenantGroupFactory.create_batch(10, has_parent=False, using=db_name)
121
- TenantGroupFactory.create_batch(10, has_parent=True, using=db_name)
122
- self.stdout.write("Creating Tenants...")
123
- TenantFactory.create_batch(10, has_tenant_group=False, using=db_name)
124
- TenantFactory.create_batch(10, has_tenant_group=True, using=db_name)
125
- self.stdout.write("Creating LocationTypes...")
126
- LocationTypeFactory.create_batch(7, using=db_name) # only 7 unique LocationTypes are hard-coded presently
127
- self.stdout.write("Creating Locations...")
135
+ _create_batch(TagFactory, 15, description="on some content-types")
136
+ _create_batch(ContactFactory, 20)
137
+ _create_batch(TeamFactory, 20)
138
+ _create_batch(TenantGroupFactory, 10, description="without parents", has_parent=False)
139
+ _create_batch(TenantGroupFactory, 10, description="with parents", has_parent=True)
140
+ _create_batch(TenantFactory, 10, description="without a parent group", has_tenant_group=False)
141
+ _create_batch(TenantFactory, 10, description="with a parent group", has_tenant_group=True)
142
+ _create_batch(LocationTypeFactory, 7) # only 7 unique LocationTypes are hard-coded presently
128
143
  # First 7 locations must be created in specific order so subsequent objects have valid parents to reference
129
- LocationFactory.create_batch(7, has_parent=True, using=db_name)
130
- LocationFactory.create_batch(40, using=db_name)
131
- LocationFactory.create_batch(10, has_parent=False, using=db_name)
132
- self.stdout.write("Creating Controller with Groups...")
133
- ControllerFactory.create_batch(1)
134
- ControllerManagedDeviceGroupFactory.create_batch(5)
135
- self.stdout.write("Creating RIRs...")
136
- RIRFactory.create_batch(9, using=db_name) # only 9 unique RIR names are hard-coded presently
137
- self.stdout.write("Creating RouteTargets...")
138
- RouteTargetFactory.create_batch(20, using=db_name)
139
- self.stdout.write("Creating Namespaces...")
140
- NamespaceFactory.create_batch(10, using=db_name)
141
- self.stdout.write("Creating VRFs...")
142
- VRFFactory.create_batch(10, has_tenant=True, using=db_name)
143
- VRFFactory.create_batch(10, has_tenant=False, using=db_name)
144
- self.stdout.write("Creating VLANGroups...")
145
- VLANGroupFactory.create_batch(20, using=db_name)
146
- self.stdout.write("Creating VLANs...")
147
- VLANFactory.create_batch(20, using=db_name)
148
- self.stdout.write("Creating Prefixes and IP Addresses...")
144
+ _create_batch(LocationFactory, 7, description="as structure", has_parent=True)
145
+ _create_batch(LocationFactory, 40)
146
+ _create_batch(LocationFactory, 10, description="without a parent Location", has_parent=False)
147
+ _create_batch(ControllerFactory, 1, description="without a Device or DeviceRedundancyGroup")
148
+ _create_batch(ControllerManagedDeviceGroupFactory, 5, description="to contain Devices")
149
+ _create_batch(RIRFactory, 9) # only 9 unique RIR names are hard-coded presently
150
+ _create_batch(RouteTargetFactory, 20)
151
+ _create_batch(NamespaceFactory, 10)
152
+ _create_batch(VRFFactory, 20)
153
+ _create_batch(VLANGroupFactory, 20)
154
+ _create_batch(VLANFactory, 20)
149
155
  for i in range(30):
150
- PrefixFactory.create(prefix=f"10.{i}.0.0/16", type=PrefixTypeChoices.TYPE_CONTAINER, using=db_name)
151
- PrefixFactory.create(prefix=f"2001:db8:0:{i}::/64", type=PrefixTypeChoices.TYPE_CONTAINER, using=db_name)
152
- self.stdout.write("Creating Empty Namespaces...")
153
- NamespaceFactory.create_batch(5, using=db_name)
154
- self.stdout.write("Creating Device Families...")
155
- DeviceFamilyFactory.create_batch(20)
156
- self.stdout.write("Creating Manufacturers...")
157
- ManufacturerFactory.create_batch(8, using=db_name) # First 8 hard-coded Manufacturers
158
- self.stdout.write("Creating Platforms (with manufacturers)...")
159
- PlatformFactory.create_batch(20, has_manufacturer=True, using=db_name)
160
- self.stdout.write("Creating Platforms (without manufacturers)...")
161
- PlatformFactory.create_batch(5, has_manufacturer=False, using=db_name)
162
- self.stdout.write("Creating SoftwareVersions...")
163
- SoftwareVersionFactory.create_batch(20)
164
- self.stdout.write("Creating SoftwareImageFiles...")
165
- SoftwareImageFileFactory.create_batch(25)
166
- self.stdout.write("Creating Manufacturers without Platforms...")
167
- ManufacturerFactory.create_batch(4, using=db_name) # 4 more hard-coded Manufacturers
168
- self.stdout.write("Creating DeviceTypes...")
169
- DeviceTypeFactory.create_batch(30, using=db_name)
170
- self.stdout.write("Creating Manufacturers without DeviceTypes or Platforms...")
171
- ManufacturerFactory.create_batch(2, using=db_name) # Last 2 hard-coded Manufacturers
172
- self.stdout.write("Creating DeviceRedundancyGroups...")
173
- DeviceRedundancyGroupFactory.create_batch(20, using=db_name)
174
- self.stdout.write("Creating Devices...")
175
- DeviceFactory.create_batch(20, using=db_name)
176
- self.stdout.write("Creating SoftwareVersions with Devices, InventoryItems or VirtualMachines...")
177
- SoftwareVersionFactory.create_batch(5)
178
- self.stdout.write("Creating SoftwareImageFiles without DeviceTypes...")
179
- SoftwareImageFileFactory.create_batch(5)
180
- self.stdout.write("Creating CircuitTypes...")
181
- CircuitTypeFactory.create_batch(40, using=db_name)
182
- self.stdout.write("Creating Providers...")
183
- ProviderFactory.create_batch(20, using=db_name)
184
- self.stdout.write("Creating ProviderNetworks...")
185
- ProviderNetworkFactory.create_batch(20, using=db_name)
186
- self.stdout.write("Creating Circuits...")
187
- CircuitFactory.create_batch(40, using=db_name)
188
- self.stdout.write("Creating Providers without Circuits...")
189
- ProviderFactory.create_batch(20, using=db_name)
190
- self.stdout.write("Creating CircuitTerminations...")
191
- CircuitTerminationFactory.create_batch(2, has_location=True, term_side="A", using=db_name)
192
- CircuitTerminationFactory.create_batch(2, has_location=True, term_side="Z", using=db_name)
193
- CircuitTerminationFactory.create_batch(2, has_location=False, term_side="A", using=db_name)
194
- CircuitTerminationFactory.create_batch(2, has_location=False, term_side="Z", using=db_name)
195
- CircuitTerminationFactory.create_batch(2, has_port_speed=True, has_upstream_speed=False, using=db_name)
196
- CircuitTerminationFactory.create_batch(
197
- size=2,
156
+ _create_batch(
157
+ PrefixFactory,
158
+ 1,
159
+ description=f"(10.{i}.0.0/16 and descendants)",
160
+ prefix=f"10.{i}.0.0/16",
161
+ type=PrefixTypeChoices.TYPE_CONTAINER,
162
+ )
163
+ _create_batch(
164
+ PrefixFactory,
165
+ 1,
166
+ description=f"(2001:db8:0:{i}::/64 and descendants)",
167
+ prefix=f"2001:db8:0:{i}::/64",
168
+ type=PrefixTypeChoices.TYPE_CONTAINER,
169
+ )
170
+ _create_batch(NamespaceFactory, 5, description="without any Prefixes or IPAddresses")
171
+ _create_batch(DeviceFamilyFactory, 20)
172
+ _create_batch(ManufacturerFactory, 8) # First 8 hard-coded Manufacturers
173
+ _create_batch(PlatformFactory, 20, description="with Manufacturers", has_manufacturer=True)
174
+ _create_batch(PlatformFactory, 5, description="without Manufacturers", has_manufacturer=False)
175
+ _create_batch(SoftwareVersionFactory, 20, description="to be usable by Devices")
176
+ _create_batch(SoftwareImageFileFactory, 25, description="to be usable by DeviceTypes")
177
+ _create_batch(ManufacturerFactory, 4, description="without Platforms") # 4 more hard-coded Manufacturers
178
+ _create_batch(DeviceTypeFactory, 30)
179
+ _create_batch(ManufacturerFactory, 2, description="without Platforms or DeviceTypes") # Last 2 hard-coded
180
+ _create_batch(DeviceRedundancyGroupFactory, 20)
181
+ _create_batch(DeviceFactory, 20)
182
+ _create_batch(SoftwareVersionFactory, 5, description="without Devices")
183
+ _create_batch(SoftwareImageFileFactory, 5, description="without DeviceTypes")
184
+ _create_batch(CircuitTypeFactory, 40)
185
+ _create_batch(ProviderFactory, 20, description="to be usable by Circuits")
186
+ _create_batch(ProviderNetworkFactory, 20)
187
+ _create_batch(CircuitFactory, 40)
188
+ _create_batch(ProviderFactory, 20, description="without Circuits")
189
+ # TODO do we really need all of these specifics for CircuitTerminations?
190
+ _create_batch(
191
+ CircuitTerminationFactory, 2, description="with a location, for side A", has_location=True, term_side="A"
192
+ )
193
+ _create_batch(
194
+ CircuitTerminationFactory, 2, description="with a location, for side Z", has_location=True, term_side="Z"
195
+ )
196
+ _create_batch(
197
+ CircuitTerminationFactory,
198
+ 2,
199
+ description="without a location, for side A",
200
+ has_location=False,
201
+ term_side="A",
202
+ )
203
+ _create_batch(
204
+ CircuitTerminationFactory,
205
+ 2,
206
+ description="without a location, for side Z",
207
+ has_location=False,
208
+ term_side="Z",
209
+ )
210
+ _create_batch(
211
+ CircuitTerminationFactory,
212
+ 2,
213
+ description="with port_speed but without upstream_speed",
214
+ has_port_speed=True,
215
+ has_upstream_speed=False,
216
+ )
217
+ _create_batch(
218
+ CircuitTerminationFactory,
219
+ 2,
220
+ description="with a location, port_speed, upstream_speed, xconnect_id, pp_info, and description",
198
221
  has_location=True,
199
222
  has_port_speed=True,
200
223
  has_upstream_speed=True,
201
224
  has_xconnect_id=True,
202
225
  has_pp_info=True,
203
226
  has_description=True,
204
- using=db_name,
205
227
  )
206
- self.stdout.write("Creating ExternalIntegrations...")
207
- ExternalIntegrationFactory.create_batch(20, using=db_name)
208
- self.stdout.write("Creating Controllers with Device or DeviceRedundancyGroups...")
209
- ControllerFactory.create_batch(10)
210
- ControllerManagedDeviceGroupFactory.create_batch(30)
228
+ _create_batch(ExternalIntegrationFactory, 20)
229
+ _create_batch(ControllerFactory, 10, description="with Devices or DeviceRedundancyGroups")
230
+ _create_batch(ControllerManagedDeviceGroupFactory, 5, description="without any Devices")
211
231
  # make sure we have some tenants that have null relationships to make filter tests happy
212
- self.stdout.write("Creating Tenants without Circuits, Locations, IPAddresses, or Prefixes...")
213
- TenantFactory.create_batch(10, using=db_name)
232
+ _create_batch(TenantFactory, 10, description="without any associated objects")
214
233
  # TODO: nautobot.tenancy.tests.test_filters currently calls the following additional factories:
215
- # UserFactory.create_batch(10)
216
- # RackFactory.create_batch(10)
217
- # RackReservationFactory.create_batch(10)
218
- # ClusterTypeFactory.create_batch(10)
219
- # ClusterGroupFactory.create_batch(10)
220
- # ClusterFactory.create_batch(10)
221
- # VirtualMachineFactory.create_batch(10)
234
+ # _create_batch(UserFactory, 10)
235
+ # _create_batch(RackFactory, 10)
236
+ # _create_batch(RackReservationFactory, 10)
237
+ # _create_batch(ClusterTypeFactory, 10)
238
+ # _create_batch(ClusterGroupFactory, 10)
239
+ # _create_batch(ClusterFactory, 10)
240
+ # _create_batch(VirtualMachineFactory, 10)
222
241
  # We need to remove them from there and enable them here instead, but that will require many test updates.
223
242
 
224
- self._output_hash_for_factory_models(
225
- factories=[
226
- CircuitFactory,
227
- CircuitTerminationFactory,
228
- CircuitTypeFactory,
229
- ContactFactory,
230
- ControllerManagedDeviceGroupFactory,
231
- ControllerFactory,
232
- DeviceFactory,
233
- DeviceFamilyFactory,
234
- DeviceRedundancyGroupFactory,
235
- DeviceTypeFactory,
236
- ExternalIntegrationFactory,
237
- IPAddressFactory,
238
- LocationFactory,
239
- LocationTypeFactory,
240
- ManufacturerFactory,
241
- NamespaceFactory,
242
- PlatformFactory,
243
- PrefixFactory,
244
- ProviderFactory,
245
- ProviderNetworkFactory,
246
- RIRFactory,
247
- RoleFactory,
248
- RouteTargetFactory,
249
- SoftwareImageFileFactory,
250
- SoftwareVersionFactory,
251
- StatusFactory,
252
- TagFactory,
253
- TeamFactory,
254
- TenantFactory,
255
- TenantGroupFactory,
256
- VLANFactory,
257
- VLANGroupFactory,
258
- VRFFactory,
259
- ]
260
- )
261
-
262
- def _output_hash_for_factory_models(self, factories):
263
- """Output a hash of the IDs of all objects in the given factories' model.
264
-
265
- Used for identifying factory determinism problems in unit tests. Only prints if GITHUB_ACTIONS environment variable is set to "true".
266
- """
267
- if not is_truthy(os.environ.get("GITHUB_ACTIONS", "false")):
268
- return
269
-
270
- for factory in factories:
271
- model = factory._meta.get_model_class()
272
- model_ids = list(model.objects.order_by("id").values_list("id", flat=True))
273
- sha256_hash = hashlib.sha256(json.dumps(model_ids, cls=DjangoJSONEncoder).encode()).hexdigest()
274
- self.stdout.write(f"SHA256 hash for {model.__name__}: {sha256_hash}")
275
-
276
243
  def handle(self, *args, **options):
277
244
  if options["flush"]:
278
245
  if options["interactive"]:
@@ -298,7 +265,10 @@ Type 'yes' to continue, or 'no' to cancel: """
298
265
  self.stdout.write(self.style.WARNING(f"Loading factory data from file {options['fixture_file']}"))
299
266
  call_command("loaddata", "--database", options["database"], options["fixture_file"])
300
267
  else:
301
- self._generate_factory_data(options["seed"], options["database"])
268
+ print_hashes = options["print_hashes"]
269
+ if is_truthy(os.environ.get("GITHUB_ACTIONS", "false")):
270
+ print_hashes = True
271
+ self._generate_factory_data(options["seed"], options["database"], print_hashes=print_hashes)
302
272
 
303
273
  if options["cache_test_fixtures"]:
304
274
  self.stdout.write(self.style.WARNING(f"Saving factory data to file {options['fixture_file']}"))
@@ -13,6 +13,7 @@ from nautobot.core.constants import CHARFIELD_MAX_LENGTH
13
13
  from nautobot.core.forms import fields, widgets
14
14
  from nautobot.core.models import ordering
15
15
  from nautobot.core.models.managers import TagsManager
16
+ from nautobot.core.models.validators import EnhancedURLValidator
16
17
 
17
18
 
18
19
  class mac_unix_expanded_uppercase(mac_unix_expanded):
@@ -381,6 +382,20 @@ class JSONArrayField(models.JSONField):
381
382
  )
382
383
 
383
384
 
385
+ class LaxURLField(models.URLField):
386
+ """Like models.URLField, but using validators.EnhancedURLValidator and forms.LaxURLField."""
387
+
388
+ default_validators = [EnhancedURLValidator()]
389
+
390
+ def formfield(self, **kwargs):
391
+ return super().formfield(
392
+ **{
393
+ "form_class": fields.LaxURLField,
394
+ **kwargs,
395
+ },
396
+ )
397
+
398
+
384
399
  class TagsField(TaggableManager):
385
400
  """Override FormField method on taggit.managers.TaggableManager to match the Nautobot UI."""
386
401
 
@@ -773,7 +773,7 @@ properties:
773
773
  description: "A mapping of permissions to assign a new user account when created using SSO authentication."
774
774
  details: |-
775
775
  Each key in the dictionary will be the permission name specified as `<app_label>.<action>_<model>`,
776
- and the value should be set to the permission [constraints](../guides/permissions.md#constraints),
776
+ and the value should be set to the permission [constraints](../guides/permissions.md#example-constraint-definitions),
777
777
  or `None` to allow all objects.
778
778
 
779
779
  Example:
@@ -1278,7 +1278,7 @@ properties:
1278
1278
 
1279
1279
  !!! note
1280
1280
  If a given device has an appropriately populated
1281
- [secrets group](../../platform-functionality/secret.md#secretsgroup) assigned to it,
1281
+ [secrets group](../../platform-functionality/secret.md#secrets-groups) assigned to it,
1282
1282
  the [secrets](../../platform-functionality/secret.md) defined in that group will take precedence
1283
1283
  over these default values.
1284
1284
  environment_variable: "NAUTOBOT_NAPALM_PASSWORD"
@@ -1300,7 +1300,7 @@ properties:
1300
1300
 
1301
1301
  !!! note
1302
1302
  If a given device has an appropriately populated
1303
- [secrets group](../../platform-functionality/secret.md#secretsgroup) assigned to it,
1303
+ [secrets group](../../platform-functionality/secret.md#secrets-groups) assigned to it,
1304
1304
  the [secrets](../../platform-functionality/secret.md) defined in that group will take precedence
1305
1305
  over these default values.
1306
1306
  environment_variable: "NAUTOBOT_NAPALM_USERNAME"
@@ -1,12 +1,19 @@
1
1
  import random
2
2
  import string
3
3
 
4
+ from django.contrib.contenttypes.models import ContentType
4
5
  from django.db.models import Count, Q
5
6
  from django.db.models.fields.related import ManyToManyField
6
7
  from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel
7
8
  from django.test import tag
8
9
 
9
- from nautobot.core.filters import RelatedMembershipBooleanFilter, SearchFilter
10
+ from nautobot.core.filters import (
11
+ ContentTypeChoiceFilter,
12
+ ContentTypeFilter,
13
+ ContentTypeMultipleChoiceFilter,
14
+ RelatedMembershipBooleanFilter,
15
+ SearchFilter,
16
+ )
10
17
  from nautobot.core.models.generics import PrimaryModel
11
18
  from nautobot.core.testing import views
12
19
  from nautobot.tenancy import models
@@ -286,6 +293,22 @@ class FilterTestCases:
286
293
  obj, obj_field_name = self._get_nested_related_obj_and_its_field_name(obj, obj_field_name)
287
294
  self._assert_q_filter_predicate_validity(obj, obj_field_name, filter_field_name, lookup_method)
288
295
 
296
+ def test_content_type_related_fields_uses_content_type_filter(self):
297
+ for field in self.queryset.model._meta.fields:
298
+ related_model = getattr(field, "related_model", None)
299
+ if not related_model or related_model != ContentType:
300
+ continue
301
+ with self.subTest(
302
+ f"Assert {self.filterset.__class__.__name__}.{field.name} implements ContentTypeFilter"
303
+ ):
304
+ filter_field = self.filterset.get_filters().get(field.name)
305
+ if not filter_field:
306
+ # This field is not part of the Filterset.
307
+ continue
308
+ self.assertIsInstance(
309
+ filter_field, (ContentTypeFilter, ContentTypeMultipleChoiceFilter, ContentTypeChoiceFilter)
310
+ )
311
+
289
312
  class NameOnlyFilterTestCase(FilterTestCase):
290
313
  """Add simple tests for filtering by name."""
291
314