nautobot 2.2.5__py3-none-any.whl → 2.2.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 (345) hide show
  1. nautobot/apps/api.py +2 -0
  2. nautobot/apps/models.py +2 -0
  3. nautobot/core/api/fields.py +13 -0
  4. nautobot/core/api/mixins.py +6 -1
  5. nautobot/core/api/schema.py +3 -1
  6. nautobot/core/api/serializers.py +7 -1
  7. nautobot/core/celery/__init__.py +1 -1
  8. nautobot/core/management/commands/generate_test_data.py +128 -158
  9. nautobot/core/models/fields.py +15 -0
  10. nautobot/core/tests/runner.py +10 -0
  11. nautobot/core/tests/test_utils.py +48 -1
  12. nautobot/core/utils/git.py +121 -49
  13. nautobot/core/utils/module_loading.py +10 -2
  14. nautobot/dcim/factory.py +1 -1
  15. nautobot/dcim/tests/test_models.py +2 -0
  16. nautobot/extras/datasources/git.py +133 -135
  17. nautobot/extras/datasources/utils.py +3 -0
  18. nautobot/extras/filters/__init__.py +1 -0
  19. nautobot/extras/forms/forms.py +16 -3
  20. nautobot/extras/jobs.py +9 -1
  21. nautobot/extras/migrations/0107_laxurlfield.py +28 -0
  22. nautobot/extras/migrations/0108_jobbutton_enabled.py +17 -0
  23. nautobot/extras/models/datasources.py +6 -4
  24. nautobot/extras/models/groups.py +9 -2
  25. nautobot/extras/models/jobs.py +30 -0
  26. nautobot/extras/models/models.py +2 -4
  27. nautobot/extras/tables.py +3 -0
  28. nautobot/extras/templates/extras/jobbutton_retrieve.html +6 -2
  29. nautobot/extras/templatetags/job_buttons.py +2 -2
  30. nautobot/extras/tests/git_data/01-valid-files/__init__.py +0 -0
  31. nautobot/extras/tests/git_data/01-valid-files/config_context_schemas/schema-1.yaml +18 -0
  32. nautobot/extras/tests/git_data/01-valid-files/config_contexts/context.yaml +12 -0
  33. nautobot/extras/tests/git_data/01-valid-files/config_contexts/devices/test-device.json +3 -0
  34. nautobot/extras/tests/git_data/01-valid-files/config_contexts/locations/Test Location.json +7 -0
  35. nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template.j2 +3 -0
  36. nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template2.html +4 -0
  37. nautobot/extras/tests/git_data/01-valid-files/export_templates/ipam/vlan/template.j2 +3 -0
  38. nautobot/extras/tests/git_data/01-valid-files/jobs/__init__.py +5 -0
  39. nautobot/extras/tests/git_data/01-valid-files/jobs/my_job.py +16 -0
  40. nautobot/extras/tests/git_data/02-invalid-files/__init__.py +0 -0
  41. nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema1.json +2 -0
  42. nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema2.json +1 -0
  43. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext1.json +2 -0
  44. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext2.json +1 -0
  45. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext3.json +3 -0
  46. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/devices/nosuchdevice.json +1 -0
  47. nautobot/extras/tests/git_data/02-invalid-files/dcim/template.j2 +0 -0
  48. nautobot/extras/tests/git_data/02-invalid-files/devices/template.j2 +0 -0
  49. nautobot/extras/tests/git_data/02-invalid-files/export_templates/dcim/nosuchmodel/template.j2 +3 -0
  50. nautobot/extras/tests/git_data/02-invalid-files/export_templates/nosuchapp/device/template.j2 +3 -0
  51. nautobot/extras/tests/git_data/02-invalid-files/jobs/__init__.py +2 -0
  52. nautobot/extras/tests/git_data/02-invalid-files/jobs/importerror.py +1 -0
  53. nautobot/extras/tests/git_data/02-invalid-files/jobs/syntaxerror.py +1 -0
  54. nautobot/extras/tests/git_helper.py +76 -0
  55. nautobot/extras/tests/test_api.py +28 -11
  56. nautobot/extras/tests/test_datasources.py +94 -276
  57. nautobot/extras/tests/test_dynamicgroups.py +8 -1
  58. nautobot/extras/tests/test_models.py +8 -3
  59. nautobot/extras/tests/test_views.py +21 -7
  60. nautobot/extras/views.py +1 -1
  61. nautobot/ipam/api/serializers.py +46 -16
  62. nautobot/ipam/api/views.py +29 -16
  63. nautobot/ipam/filters.py +9 -1
  64. nautobot/ipam/forms.py +8 -0
  65. nautobot/ipam/tables.py +1 -1
  66. nautobot/ipam/tests/test_api.py +15 -20
  67. nautobot/ipam/tests/test_filters.py +15 -0
  68. nautobot/project-static/docs/404.html +83 -8
  69. nautobot/project-static/docs/apps/index.html +96 -10
  70. nautobot/project-static/docs/apps/nautobot-apps.html +96 -10
  71. nautobot/project-static/docs/assets/app-icons/icon-CapacityMetrics.svg +1 -0
  72. nautobot/project-static/docs/assets/app-icons/icon-CircuitMaintenance.png +0 -0
  73. nautobot/project-static/docs/assets/javascripts/{bundle.ebd0bdb7.min.js → bundle.fe8b6f2b.min.js} +4 -4
  74. nautobot/project-static/docs/assets/javascripts/{bundle.ebd0bdb7.min.js.map → bundle.fe8b6f2b.min.js.map} +3 -3
  75. nautobot/project-static/docs/assets/javascripts/glightbox.min.js +1 -0
  76. nautobot/project-static/docs/assets/stylesheets/glightbox.min.css +1 -0
  77. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +96 -10
  78. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +96 -10
  79. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +96 -10
  80. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +96 -10
  81. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +96 -10
  82. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +96 -10
  83. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +96 -10
  84. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +96 -10
  85. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +96 -10
  86. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +96 -10
  87. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +96 -10
  88. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +96 -10
  89. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +96 -10
  90. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +96 -10
  91. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +156 -12
  92. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +96 -10
  93. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +96 -10
  94. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +96 -10
  95. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +106 -11
  96. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +96 -10
  97. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +96 -10
  98. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +143 -11
  99. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +96 -10
  100. nautobot/project-static/docs/development/apps/api/configuration-view.html +96 -10
  101. nautobot/project-static/docs/development/apps/api/database-backend-config.html +96 -10
  102. nautobot/project-static/docs/development/apps/api/models/django-admin.html +96 -10
  103. nautobot/project-static/docs/development/apps/api/models/global-search.html +96 -10
  104. nautobot/project-static/docs/development/apps/api/models/graphql.html +96 -10
  105. nautobot/project-static/docs/development/apps/api/models/index.html +96 -10
  106. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +96 -10
  107. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +96 -10
  108. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +96 -10
  109. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +96 -10
  110. nautobot/project-static/docs/development/apps/api/platform-features/index.html +96 -10
  111. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +96 -10
  112. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +96 -10
  113. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +96 -10
  114. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +96 -10
  115. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +96 -10
  116. nautobot/project-static/docs/development/apps/api/prometheus.html +96 -10
  117. nautobot/project-static/docs/development/apps/api/setup.html +96 -10
  118. nautobot/project-static/docs/development/apps/api/testing.html +96 -10
  119. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +96 -10
  120. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +96 -10
  121. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +96 -10
  122. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +96 -10
  123. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +96 -10
  124. nautobot/project-static/docs/development/apps/api/views/base-template.html +96 -10
  125. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +96 -10
  126. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +96 -10
  127. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +96 -10
  128. nautobot/project-static/docs/development/apps/api/views/index.html +96 -10
  129. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +96 -10
  130. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +96 -10
  131. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +96 -10
  132. nautobot/project-static/docs/development/apps/api/views/notes.html +96 -10
  133. nautobot/project-static/docs/development/apps/api/views/rest-api.html +96 -10
  134. nautobot/project-static/docs/development/apps/api/views/urls.html +96 -10
  135. nautobot/project-static/docs/development/apps/index.html +96 -10
  136. nautobot/project-static/docs/development/apps/migration/code-updates.html +97 -11
  137. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +96 -10
  138. nautobot/project-static/docs/development/apps/migration/from-v1.html +96 -10
  139. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +96 -10
  140. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +96 -10
  141. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +96 -10
  142. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +96 -10
  143. nautobot/project-static/docs/development/apps/porting-from-netbox.html +96 -10
  144. nautobot/project-static/docs/development/core/application-registry.html +96 -10
  145. nautobot/project-static/docs/development/core/best-practices.html +96 -10
  146. nautobot/project-static/docs/development/core/bootstrap-ui.html +96 -10
  147. nautobot/project-static/docs/development/core/caching.html +96 -10
  148. nautobot/project-static/docs/development/core/controllers.html +96 -10
  149. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +96 -10
  150. nautobot/project-static/docs/development/core/generic-views.html +96 -10
  151. nautobot/project-static/docs/development/core/getting-started.html +98 -13
  152. nautobot/project-static/docs/development/core/homepage.html +96 -10
  153. nautobot/project-static/docs/development/core/index.html +96 -10
  154. nautobot/project-static/docs/development/core/model-checklist.html +96 -10
  155. nautobot/project-static/docs/development/core/model-features.html +96 -10
  156. nautobot/project-static/docs/development/core/natural-keys.html +96 -10
  157. nautobot/project-static/docs/development/core/navigation-menu.html +96 -10
  158. nautobot/project-static/docs/development/core/release-checklist.html +96 -10
  159. nautobot/project-static/docs/development/core/role-internals.html +96 -10
  160. nautobot/project-static/docs/development/core/settings.html +96 -10
  161. nautobot/project-static/docs/development/core/style-guide.html +96 -10
  162. nautobot/project-static/docs/development/core/templates.html +96 -10
  163. nautobot/project-static/docs/development/core/testing.html +109 -11
  164. nautobot/project-static/docs/development/core/user-preferences.html +96 -10
  165. nautobot/project-static/docs/development/index.html +96 -10
  166. nautobot/project-static/docs/development/jobs/index.html +96 -10
  167. nautobot/project-static/docs/development/jobs/migration/from-v1.html +96 -10
  168. nautobot/project-static/docs/index.html +13 -8362
  169. nautobot/project-static/docs/objects.inv +0 -0
  170. nautobot/project-static/docs/overview/application_stack.html +8229 -0
  171. nautobot/project-static/docs/overview/design_philosophy.html +8158 -0
  172. nautobot/project-static/docs/overview/index.html +8230 -0
  173. nautobot/project-static/docs/release-notes/index.html +96 -10
  174. nautobot/project-static/docs/release-notes/version-1.0.html +96 -10
  175. nautobot/project-static/docs/release-notes/version-1.1.html +96 -10
  176. nautobot/project-static/docs/release-notes/version-1.2.html +96 -10
  177. nautobot/project-static/docs/release-notes/version-1.3.html +96 -10
  178. nautobot/project-static/docs/release-notes/version-1.4.html +96 -10
  179. nautobot/project-static/docs/release-notes/version-1.5.html +96 -10
  180. nautobot/project-static/docs/release-notes/version-1.6.html +96 -10
  181. nautobot/project-static/docs/release-notes/version-2.0.html +96 -10
  182. nautobot/project-static/docs/release-notes/version-2.1.html +96 -10
  183. nautobot/project-static/docs/release-notes/version-2.2.html +516 -131
  184. nautobot/project-static/docs/requirements.txt +2 -1
  185. nautobot/project-static/docs/search/search_index.json +1 -1
  186. nautobot/project-static/docs/sitemap.xml +268 -258
  187. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  188. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +96 -10
  189. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +96 -10
  190. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +96 -10
  191. nautobot/project-static/docs/user-guide/administration/configuration/index.html +96 -10
  192. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +96 -10
  193. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +96 -10
  194. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +96 -10
  195. nautobot/project-static/docs/user-guide/administration/guides/caching.html +96 -10
  196. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +96 -10
  197. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +96 -10
  198. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +96 -10
  199. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +96 -10
  200. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +96 -10
  201. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +96 -10
  202. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +96 -10
  203. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +96 -10
  204. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +96 -10
  205. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +96 -10
  206. nautobot/project-static/docs/user-guide/administration/installation/index.html +96 -10
  207. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +96 -10
  208. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +96 -10
  209. nautobot/project-static/docs/user-guide/administration/installation/services.html +96 -10
  210. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +96 -10
  211. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +96 -10
  212. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +96 -10
  213. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +96 -10
  214. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +96 -10
  215. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +96 -10
  216. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +96 -10
  217. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +96 -10
  218. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +96 -10
  219. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +96 -10
  220. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +96 -10
  221. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +96 -10
  222. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +96 -10
  223. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +96 -10
  224. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-location-changes.yaml +1 -1
  225. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +97 -11
  226. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +96 -10
  227. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +96 -10
  228. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +96 -10
  229. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +96 -10
  230. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +96 -10
  231. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +96 -10
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +96 -10
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +96 -10
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +96 -10
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +96 -10
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +96 -10
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +96 -10
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +96 -10
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +96 -10
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +96 -10
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +96 -10
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +96 -10
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +96 -10
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +96 -10
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +96 -10
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +96 -10
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +96 -10
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +96 -10
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +96 -10
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +96 -10
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +96 -10
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +96 -10
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +96 -10
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +96 -10
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +96 -10
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +96 -10
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +96 -10
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +96 -10
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +96 -10
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +96 -10
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +96 -10
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +96 -10
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +96 -10
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +96 -10
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +96 -10
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +96 -10
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +96 -10
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +96 -10
  269. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +96 -10
  270. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +96 -10
  271. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +96 -10
  272. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +96 -10
  273. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +96 -10
  274. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +96 -10
  275. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +96 -10
  276. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +96 -10
  277. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +96 -10
  278. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +96 -10
  279. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +96 -10
  280. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +96 -10
  281. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +96 -10
  282. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +96 -10
  283. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +96 -10
  284. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +96 -10
  285. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +96 -10
  286. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +96 -10
  287. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +96 -10
  288. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +96 -10
  289. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +96 -10
  290. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +96 -10
  291. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +96 -10
  292. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +96 -10
  293. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +96 -10
  294. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +96 -10
  295. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +96 -10
  296. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +96 -10
  297. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +96 -10
  298. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +96 -10
  299. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +96 -10
  300. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +96 -10
  301. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +96 -10
  302. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +96 -10
  303. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +96 -10
  304. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +96 -10
  305. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +96 -10
  306. nautobot/project-static/docs/user-guide/index.html +99 -13
  307. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +96 -10
  308. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +96 -10
  309. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +96 -10
  310. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +96 -10
  311. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +96 -10
  312. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +96 -10
  313. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +96 -10
  314. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +96 -10
  315. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +96 -10
  316. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +96 -10
  317. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +96 -10
  318. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +97 -11
  319. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +96 -10
  320. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +101 -14
  321. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +96 -10
  322. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +96 -10
  323. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +96 -10
  324. nautobot/project-static/docs/user-guide/platform-functionality/note.html +96 -10
  325. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +96 -10
  326. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +96 -10
  327. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +96 -10
  328. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +96 -10
  329. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +96 -10
  330. nautobot/project-static/docs/user-guide/platform-functionality/role.html +96 -10
  331. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +96 -10
  332. nautobot/project-static/docs/user-guide/platform-functionality/status.html +96 -10
  333. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +96 -10
  334. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +96 -10
  335. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +96 -10
  336. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +96 -10
  337. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +96 -10
  338. nautobot/project-static/js/connection_toggles.js +7 -6
  339. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/METADATA +2 -2
  340. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/RECORD +344 -311
  341. nautobot/extras/tests/test_git.py +0 -23
  342. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/LICENSE.txt +0 -0
  343. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/NOTICE +0 -0
  344. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/WHEEL +0 -0
  345. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/entry_points.txt +0 -0
@@ -254,7 +254,7 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
254
254
  slug="test_git_repo",
255
255
  remote_url="http://localhost/git.git",
256
256
  )
257
- repo.save()
257
+ repo.validated_save()
258
258
 
259
259
  with self.assertRaises(ValidationError):
260
260
  nonduplicate_context = ConfigContext(name="context 1", weight=300, data={"a": "22"}, owner=repo)
@@ -864,7 +864,7 @@ class ExportTemplateTest(ModelTestCases.BaseModelTestCase):
864
864
  slug="test_git_repo",
865
865
  remote_url="http://localhost/git.git",
866
866
  )
867
- repo.save()
867
+ repo.validated_save()
868
868
 
869
869
  with self.assertRaises(ValidationError):
870
870
  nonduplicate_template = ExportTemplate(
@@ -888,7 +888,7 @@ class ExternalIntegrationTest(ModelTestCases.BaseModelTestCase):
888
888
  )
889
889
  ei.validated_save()
890
890
 
891
- ei.remote_url = "http://localhost"
891
+ ei.remote_url = "http://some-local-host"
892
892
  ei.validated_save()
893
893
 
894
894
  def test_timeout_validation(self):
@@ -1045,6 +1045,11 @@ class GitRepositoryTest(ModelTestCases.BaseModelTestCase):
1045
1045
  repo.validated_save()
1046
1046
  self.assertIn("Please choose a different slug", str(handler.exception))
1047
1047
 
1048
+ def test_remote_url_hostname(self):
1049
+ """Confirm that a bare hostname (no domain name) can be used for a remote URL."""
1050
+ self.repo.remote_url = "http://some-private-host/example.git"
1051
+ self.repo.validated_save()
1052
+
1048
1053
 
1049
1054
  class JobModelTest(ModelTestCases.BaseModelTestCase):
1050
1055
  """
@@ -996,7 +996,7 @@ class GitRepositoryTestCase(
996
996
  # Create four GitRepository records
997
997
  repos = (
998
998
  GitRepository(name="Repo 1", slug="repo_1", remote_url="https://example.com/repo1.git"),
999
- GitRepository(name="Repo 2", slug="repo_2", remote_url="https://example.com/repo2.git"),
999
+ GitRepository(name="Repo 2", slug="repo_2", remote_url="https://some-local-host/repo2.git"),
1000
1000
  GitRepository(name="Repo 3", slug="repo_3", remote_url="https://example.com/repo3.git"),
1001
1001
  GitRepository(name="Repo 4", remote_url="https://example.com/repo4.git", secrets_group=secrets_groups[0]),
1002
1002
  )
@@ -1006,7 +1006,7 @@ class GitRepositoryTestCase(
1006
1006
  cls.form_data = {
1007
1007
  "name": "A new Git repository",
1008
1008
  "slug": "a_new_git_repository",
1009
- "remote_url": "http://example.com/a_new_git_repository.git",
1009
+ "remote_url": "http://another-local-host/a_new_git_repository.git",
1010
1010
  "branch": "develop",
1011
1011
  "_token": "1234567890abcdef1234567890abcdef",
1012
1012
  "secrets_group": secrets_groups[1].pk,
@@ -2329,23 +2329,30 @@ class JobButtonTestCase(
2329
2329
 
2330
2330
  @classmethod
2331
2331
  def setUpTestData(cls):
2332
+ jbr_simple = Job.objects.get(job_class_name="TestJobButtonReceiverSimple")
2333
+ jbr_simple.enabled = True
2334
+ jbr_simple.save()
2335
+ jbr_complex = Job.objects.get(job_class_name="TestJobButtonReceiverComplex")
2336
+ jbr_complex.enabled = True
2337
+ jbr_complex.save()
2338
+
2332
2339
  job_buttons = (
2333
2340
  JobButton.objects.create(
2334
2341
  name="JobButton1",
2335
2342
  text="JobButton1",
2336
- job=Job.objects.get(job_class_name="TestJobButtonReceiverSimple"),
2343
+ job=jbr_simple,
2337
2344
  confirmation=True,
2338
2345
  ),
2339
2346
  JobButton.objects.create(
2340
2347
  name="JobButton2",
2341
2348
  text="JobButton2",
2342
- job=Job.objects.get(job_class_name="TestJobButtonReceiverSimple"),
2349
+ job=jbr_simple,
2343
2350
  confirmation=False,
2344
2351
  ),
2345
2352
  JobButton.objects.create(
2346
2353
  name="JobButton3",
2347
2354
  text="JobButton3",
2348
- job=Job.objects.get(job_class_name="TestJobButtonReceiverComplex"),
2355
+ job=jbr_complex,
2349
2356
  confirmation=True,
2350
2357
  weight=50,
2351
2358
  ),
@@ -2359,7 +2366,7 @@ class JobButtonTestCase(
2359
2366
  "content_types": [location_ct.pk],
2360
2367
  "name": "jobbutton-4",
2361
2368
  "text": "jobbutton text 4",
2362
- "job": Job.objects.get(job_class_name="TestJobButtonReceiverComplex").pk,
2369
+ "job": jbr_complex.pk,
2363
2370
  "weight": 100,
2364
2371
  "button_class": "default",
2365
2372
  "confirmation": False,
@@ -2374,6 +2381,9 @@ class JobButtonRenderingTestCase(TestCase):
2374
2381
  def setUp(self):
2375
2382
  super().setUp()
2376
2383
  self.job = Job.objects.get(job_class_name="TestJobButtonReceiverSimple")
2384
+ self.job.enabled = True
2385
+ self.job.save()
2386
+
2377
2387
  self.job_button_1 = JobButton(
2378
2388
  name="JobButton 1",
2379
2389
  text="JobButton {{ obj.name }}",
@@ -2383,10 +2393,14 @@ class JobButtonRenderingTestCase(TestCase):
2383
2393
  self.job_button_1.validated_save()
2384
2394
  self.job_button_1.content_types.add(ContentType.objects.get_for_model(LocationType))
2385
2395
 
2396
+ job_2 = Job.objects.get(job_class_name="TestJobButtonReceiverComplex")
2397
+ job_2.enabled = True
2398
+ job_2.save()
2399
+
2386
2400
  self.job_button_2 = JobButton(
2387
2401
  name="JobButton 2",
2388
2402
  text="Click me!",
2389
- job=Job.objects.get(job_class_name="TestJobButtonReceiverComplex"),
2403
+ job=job_2,
2390
2404
  confirmation=False,
2391
2405
  )
2392
2406
  self.job_button_2.validated_save()
nautobot/extras/views.py CHANGED
@@ -1069,7 +1069,7 @@ def check_and_call_git_repository_function(request, pk, func):
1069
1069
  # Allow execution only if a worker process is running.
1070
1070
  if not get_worker_count():
1071
1071
  messages.error(request, "Unable to run job: Celery worker process not running.")
1072
- return redirect(request.get_full_path(), permanent=False)
1072
+ return redirect(reverse("extras:gitrepository", args=(pk,)), permanent=False)
1073
1073
  else:
1074
1074
  repository = get_object_or_404(GitRepository.objects.restrict(request.user, "change"), pk=pk)
1075
1075
  job_result = func(repository, request.user)
@@ -321,27 +321,30 @@ class PrefixLocationAssignmentSerializer(ValidatedModelSerializer):
321
321
  fields = "__all__"
322
322
 
323
323
 
324
- class PrefixLengthSerializer(serializers.Serializer):
325
- prefix_length = serializers.IntegerField()
326
-
327
- def to_internal_value(self, data):
328
- requested_prefix = data.get("prefix_length")
329
- if requested_prefix is None:
330
- raise serializers.ValidationError({"prefix_length": "this field can not be missing"})
331
- if not isinstance(requested_prefix, int):
332
- raise serializers.ValidationError({"prefix_length": "this field must be int type"})
333
-
334
- prefix = self.context.get("prefix")
335
- if prefix.ip_version == 4 and requested_prefix > 32:
336
- raise serializers.ValidationError({"prefix_length": f"Invalid prefix length ({requested_prefix}) for IPv4"})
337
- elif prefix.ip_version == 6 and requested_prefix > 128:
338
- raise serializers.ValidationError({"prefix_length": f"Invalid prefix length ({requested_prefix}) for IPv6"})
339
- return data
324
+ class PrefixLengthSerializer(PrefixLegacySerializer):
325
+ """
326
+ Input serializer for POST to /api/ipam/prefixes/<id>/available-prefixes/, i.e. allocating one or more sub-prefixes.
327
+
328
+ Since setting of multiple locations on create is not supported, this uses the legacy single-location option.
329
+ """
330
+
331
+ prefix_length = serializers.IntegerField(required=True)
332
+
333
+ class Meta(PrefixLegacySerializer.Meta):
334
+ fields = PrefixLegacySerializer.Meta.fields.copy()
335
+ fields.remove("prefix")
336
+ fields.remove("network")
337
+ fields.remove("broadcast")
338
+ fields.remove("parent")
339
+ fields.remove("ip_version")
340
+ fields.remove("namespace")
340
341
 
341
342
 
342
343
  class AvailablePrefixSerializer(serializers.Serializer):
343
344
  """
344
345
  Representation of a prefix which does not exist in the database.
346
+
347
+ Response serializer for a GET to /api/ipam/prefixes/<id>/available-prefixes/.
345
348
  """
346
349
 
347
350
  ip_version = serializers.IntegerField(read_only=True)
@@ -437,6 +440,8 @@ class IPAddressSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
437
440
  class AvailableIPSerializer(serializers.Serializer):
438
441
  """
439
442
  Representation of an IP address which does not exist in the database.
443
+
444
+ Response serializer for a GET to /api/ipam/prefixes/<id>/available-ips/.
440
445
  """
441
446
 
442
447
  ip_version = serializers.IntegerField(read_only=True)
@@ -451,6 +456,31 @@ class AvailableIPSerializer(serializers.Serializer):
451
456
  )
452
457
 
453
458
 
459
+ class IPAllocationSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
460
+ """
461
+ Input serializer for POST to /api/ipam/prefixes/<id>/available-ips/, i.e. allocating addresses from a prefix.
462
+ """
463
+
464
+ class Meta:
465
+ model = IPAddress
466
+ fields = (
467
+ # not address/namespace/parent as those are implied by the selected prefix
468
+ "status",
469
+ "type",
470
+ "dns_name",
471
+ "description",
472
+ "role",
473
+ "tenant",
474
+ "nat_inside",
475
+ "tags",
476
+ "custom_fields",
477
+ )
478
+
479
+ def validate(self, data):
480
+ data["mask_length"] = self.context["prefix"].prefix_length
481
+ return super().validate(data)
482
+
483
+
454
484
  #
455
485
  # IP address to interface
456
486
  #
@@ -121,8 +121,6 @@ class PrefixViewSet(NautobotModelViewSet):
121
121
  filterset_class = filters.PrefixFilterSet
122
122
 
123
123
  def get_serializer_class(self):
124
- if self.action == "available_prefixes" and self.request.method == "POST":
125
- return serializers.PrefixLengthSerializer
126
124
  if (
127
125
  not getattr(self, "swagger_fake_view", False)
128
126
  and self.request.major_version == 2
@@ -159,7 +157,11 @@ class PrefixViewSet(NautobotModelViewSet):
159
157
  raise self.LocationIncompatibleLegacyBehavior from e
160
158
 
161
159
  @extend_schema(methods=["get"], responses={200: serializers.AvailablePrefixSerializer(many=True)})
162
- @extend_schema(methods=["post"], responses={201: serializers.PrefixSerializer(many=False)})
160
+ @extend_schema(
161
+ methods=["post"],
162
+ request=serializers.PrefixLengthSerializer,
163
+ responses={201: serializers.PrefixSerializer(many=True)},
164
+ )
163
165
  @action(
164
166
  detail=True,
165
167
  name="Available Prefixes",
@@ -169,10 +171,10 @@ class PrefixViewSet(NautobotModelViewSet):
169
171
  )
170
172
  def available_prefixes(self, request, pk=None):
171
173
  """
172
- A convenience method for returning available child prefixes within a parent.
174
+ A convenience method for listing and/or allocating available child prefixes within a parent.
173
175
 
174
- The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
175
- invoked in parallel, which results in a race condition where multiple insertions can occur.
176
+ This uses a Redis lock to prevent this API from being invoked in parallel, in order to avoid a race condition
177
+ if multiple clients tried to simultaneously request allocation from the same parent prefix.
176
178
  """
177
179
  prefix = get_object_or_404(self.queryset, pk=pk)
178
180
  if request.method == "POST":
@@ -214,9 +216,9 @@ class PrefixViewSet(NautobotModelViewSet):
214
216
  # Initialize the serializer with a list or a single object depending on what was requested
215
217
  context = {"request": request, "depth": 0}
216
218
  if isinstance(request.data, list):
217
- serializer = serializers.PrefixSerializer(data=requested_prefixes, many=True, context=context)
219
+ serializer = self.get_serializer_class()(data=requested_prefixes, many=True, context=context)
218
220
  else:
219
- serializer = serializers.PrefixSerializer(data=requested_prefixes[0], context=context)
221
+ serializer = self.get_serializer_class()(data=requested_prefixes[0], context=context)
220
222
 
221
223
  # Create the new Prefix(es)
222
224
  serializer.is_valid(raise_exception=True)
@@ -238,8 +240,8 @@ class PrefixViewSet(NautobotModelViewSet):
238
240
  @extend_schema(methods=["get"], responses={200: serializers.AvailableIPSerializer(many=True)})
239
241
  @extend_schema(
240
242
  methods=["post"],
241
- responses={201: serializers.AvailableIPSerializer(many=True)},
242
- request=serializers.AvailableIPSerializer(many=True),
243
+ responses={201: serializers.IPAddressSerializer(many=True)},
244
+ request=serializers.IPAllocationSerializer(many=True),
243
245
  )
244
246
  @action(
245
247
  detail=True,
@@ -251,12 +253,13 @@ class PrefixViewSet(NautobotModelViewSet):
251
253
  )
252
254
  def available_ips(self, request, pk=None):
253
255
  """
254
- A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
255
- returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
256
- however results will not be paginated.
256
+ A convenience method for listing and/or allocating available IP addresses within a prefix.
257
+
258
+ By default, the number of IPs returned will be equivalent to PAGINATE_COUNT.
259
+ An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed, however results will not be paginated.
257
260
 
258
- The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
259
- invoked in parallel, which results in a race condition where multiple insertions can occur.
261
+ This uses a Redis lock to prevent this API from being invoked in parallel, in order to avoid a race condition
262
+ if multiple clients tried to simultaneously request allocation from the same parent prefix.
260
263
  """
261
264
  prefix = get_object_or_404(Prefix.objects.restrict(request.user), pk=pk)
262
265
 
@@ -266,7 +269,17 @@ class PrefixViewSet(NautobotModelViewSet):
266
269
  "nautobot.ipam.api.views.available_ips", blocking_timeout=5, timeout=settings.REDIS_LOCK_TIMEOUT
267
270
  ):
268
271
  # Normalize to a list of objects
269
- requested_ips = request.data if isinstance(request.data, list) else [request.data]
272
+ serializer = serializers.IPAllocationSerializer(
273
+ data=request.data if isinstance(request.data, list) else [request.data],
274
+ many=True,
275
+ context={
276
+ "request": request,
277
+ "prefix": prefix,
278
+ },
279
+ )
280
+ serializer.is_valid(raise_exception=True)
281
+
282
+ requested_ips = serializer.validated_data
270
283
 
271
284
  # Determine if the requested number of IPs is available
272
285
  available_ips = prefix.get_available_ips()
nautobot/ipam/filters.py CHANGED
@@ -433,11 +433,19 @@ class IPAddressFilterSet(
433
433
  method="_has_interface_assignments",
434
434
  label="Has Interface Assignments",
435
435
  )
436
+ nat_inside = django_filters.ModelMultipleChoiceFilter(
437
+ queryset=IPAddress.objects.all(),
438
+ label="NAT (Inside)",
439
+ )
440
+ has_nat_inside = RelatedMembershipBooleanFilter(
441
+ field_name="nat_inside",
442
+ label="Has NAT Inside",
443
+ )
436
444
  ip_version = django_filters.NumberFilter()
437
445
 
438
446
  class Meta:
439
447
  model = IPAddress
440
- fields = ["id", "dns_name", "type", "tags", "mask_length"]
448
+ fields = ["id", "dns_name", "type", "tags", "mask_length", "nat_inside"]
441
449
 
442
450
  def generate_query__has_interface_assignments(self, value):
443
451
  """Helper method used by DynamicGroups and by _assigned_to_interface method."""
nautobot/ipam/forms.py CHANGED
@@ -664,6 +664,8 @@ class IPAddressFilterForm(NautobotFilterForm, TenancyFilterForm, StatusModelFilt
664
664
  "role",
665
665
  "tenant_group",
666
666
  "tenant",
667
+ "nat_inside",
668
+ "has_nat_inside",
667
669
  ]
668
670
  q = forms.CharField(required=False, label="Search")
669
671
  parent = forms.CharField(
@@ -700,6 +702,12 @@ class IPAddressFilterForm(NautobotFilterForm, TenancyFilterForm, StatusModelFilt
700
702
  widget=StaticSelect2(),
701
703
  )
702
704
  tags = TagFilterField(model)
705
+ nat_inside = DynamicModelChoiceField(queryset=IPAddress.objects.all(), required=False, label="NAT Inside Address")
706
+ has_nat_inside = forms.NullBooleanField(
707
+ required=False,
708
+ label="Has NAT Inside",
709
+ widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES),
710
+ )
703
711
 
704
712
 
705
713
  #
nautobot/ipam/tables.py CHANGED
@@ -473,7 +473,7 @@ class IPAddressTable(StatusTableMixin, RoleTableMixin, BaseTable):
473
473
 
474
474
 
475
475
  class IPAddressDetailTable(IPAddressTable):
476
- nat_inside = tables.Column(linkify=True, orderable=False, verbose_name="NAT (Inside)")
476
+ nat_inside = tables.Column(linkify=True, verbose_name="NAT (Inside)")
477
477
  tenant = TenantColumn()
478
478
  tags = TagColumn(url_name="ipam:ipaddress_list")
479
479
  assigned = BooleanColumn(accessor="assigned_count")
@@ -454,7 +454,6 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
454
454
  for i in range(4):
455
455
  data = {
456
456
  "prefix_length": child_prefix_length,
457
- "namespace": self.namespace.pk,
458
457
  "status": self.status.pk,
459
458
  "description": f"Test Prefix {i + 1}",
460
459
  }
@@ -464,13 +463,18 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
464
463
  self.assertEqual(str(response.data["namespace"]["url"]), self.absolute_api_url(prefix.namespace))
465
464
  self.assertEqual(response.data["description"], data["description"])
466
465
 
467
- # Try to create one more prefix
468
- response = self.client.post(url, {"prefix_length": child_prefix_length}, format="json", **self.header)
466
+ # Try to create one more prefix, and expect a HTTP 204 response.
467
+ # This feels wrong to me (shouldn't it be a 4xx or 5xx?) but it's how the API has historically behaved.
468
+ response = self.client.post(
469
+ url, {"prefix_length": child_prefix_length, "status": self.status.pk}, format="json", **self.header
470
+ )
469
471
  self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
470
472
  self.assertIn("detail", response.data)
471
473
 
472
- # Try to create invalid prefix type
473
- response = self.client.post(url, {"prefix_length": str(child_prefix_length)}, format="json", **self.header)
474
+ # Invalid data does trigger a HTTP 400 response.
475
+ response = self.client.post(
476
+ url, {"prefix_length": "hello", "status": self.status.pk}, format="json", **self.header
477
+ )
474
478
  self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
475
479
  self.assertIn("prefix_length", response.data[0])
476
480
 
@@ -495,35 +499,31 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
495
499
  {
496
500
  "prefix_length": child_prefix_length,
497
501
  "description": "Test Prefix 1",
498
- "namespace": self.namespace.pk,
499
502
  "status": self.status.pk,
500
503
  },
501
504
  {
502
505
  "prefix_length": child_prefix_length,
503
506
  "description": "Test Prefix 2",
504
- "namespace": self.namespace.pk,
505
507
  "status": self.status.pk,
506
508
  },
507
509
  {
508
510
  "prefix_length": child_prefix_length,
509
511
  "description": "Test Prefix 3",
510
- "namespace": self.namespace.pk,
511
512
  "status": self.status.pk,
512
513
  },
513
514
  {
514
515
  "prefix_length": child_prefix_length,
515
516
  "description": "Test Prefix 4",
516
- "namespace": self.namespace.pk,
517
517
  "status": self.status.pk,
518
518
  },
519
519
  {
520
520
  "prefix_length": child_prefix_length,
521
521
  "description": "Test Prefix 5",
522
- "namespace": self.namespace.pk,
523
522
  "status": self.status.pk,
524
523
  },
525
524
  ]
526
525
  response = self.client.post(url, data, format="json", **self.header)
526
+ # This feels wrong to me (shouldn't it be a 4xx or 5xx?) but it's how the API has historically behaved.
527
527
  self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
528
528
  self.assertIn("detail", response.data)
529
529
 
@@ -577,7 +577,6 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
577
577
  for i in range(1, 7):
578
578
  data = {
579
579
  "description": f"Test IP {i}",
580
- "namespace": self.namespace.pk,
581
580
  "status": self.status.pk,
582
581
  }
583
582
  response = self.client.post(url, data, format="json", **self.header)
@@ -586,7 +585,8 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
586
585
  self.assertEqual(response.data["description"], data["description"])
587
586
 
588
587
  # Try to create one more IP
589
- response = self.client.post(url, {}, format="json", **self.header)
588
+ response = self.client.post(url, {"status": self.status.pk}, format="json", **self.header)
589
+ # This feels wrong to me (shouldn't it be a 4xx or 5xx?) but it's how the API has historically behaved.
590
590
  self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
591
591
  self.assertIn("detail", response.data)
592
592
 
@@ -604,19 +604,14 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
604
604
  self.add_permissions("ipam.view_prefix", "ipam.add_ipaddress", "extras.view_status")
605
605
 
606
606
  # Try to create seven IPs (only six are available)
607
- data = [
608
- {"description": f"Test IP {i}", "namespace": self.namespace.pk, "status": self.status.pk}
609
- for i in range(1, 8)
610
- ] # 7 IPs
607
+ data = [{"description": f"Test IP {i}", "status": self.status.pk} for i in range(1, 8)] # 7 IPs
611
608
  response = self.client.post(url, data, format="json", **self.header)
609
+ # This feels wrong to me (shouldn't it be a 4xx or 5xx?) but it's how the API has historically behaved.
612
610
  self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
613
611
  self.assertIn("detail", response.data)
614
612
 
615
613
  # Create all six available IPs in a single request
616
- data = [
617
- {"description": f"Test IP {i}", "namespace": self.namespace.pk, "status": self.status.pk}
618
- for i in range(1, 7)
619
- ] # 6 IPs
614
+ data = [{"description": f"Test IP {i}", "status": self.status.pk} for i in range(1, 7)] # 6 IPs
620
615
  response = self.client.post(url, data, format="json", **self.header)
621
616
  self.assertHttpStatus(response, status.HTTP_201_CREATED)
622
617
  self.assertEqual(len(response.data), 6)
@@ -462,6 +462,7 @@ class IPAddressTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyF
462
462
  queryset = IPAddress.objects.all()
463
463
  filterset = IPAddressFilterSet
464
464
  tenancy_related_name = "ip_addresses"
465
+ generic_filter_tests = (["nat_inside", "nat_inside__id"],)
465
466
 
466
467
  @classmethod
467
468
  def setUpTestData(cls):
@@ -638,6 +639,20 @@ class IPAddressTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyF
638
639
  status=statuses[0],
639
640
  namespace=cls.namespace,
640
641
  )
642
+ IPAddress.objects.create(
643
+ address="10.1.1.1/32",
644
+ tenant=None,
645
+ status=statuses[0],
646
+ namespace=cls.namespace,
647
+ nat_inside=ip0,
648
+ )
649
+ IPAddress.objects.create(
650
+ address="10.2.2.2/32",
651
+ tenant=None,
652
+ status=statuses[0],
653
+ namespace=cls.namespace,
654
+ nat_inside=ip1,
655
+ )
641
656
 
642
657
  def test_search(self):
643
658
  ipv4_octets = self.ipv4_address.host.split(".")
@@ -12,7 +12,7 @@
12
12
 
13
13
 
14
14
  <link rel="icon" href="/projects/core/en/stable/assets/favicon.ico">
15
- <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.23">
15
+ <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.28">
16
16
 
17
17
 
18
18
 
@@ -91,7 +91,7 @@
91
91
 
92
92
  <header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
93
93
  <nav class="md-header__inner md-grid" aria-label="Header">
94
- <a href="/projects/core/en/stable/index.html" title="Nautobot Documentation" class="md-header__button md-logo" aria-label="Nautobot Documentation" data-md-component="logo">
94
+ <a href="/projects/core/en/stable/." title="Nautobot Documentation" class="md-header__button md-logo" aria-label="Nautobot Documentation" data-md-component="logo">
95
95
 
96
96
  <img src="/projects/core/en/stable/assets/nautobot_logo.svg" alt="logo">
97
97
 
@@ -180,7 +180,7 @@
180
180
 
181
181
  </form>
182
182
  <div class="md-search__output">
183
- <div class="md-search__scrollwrap" data-md-scrollfix>
183
+ <div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
184
184
  <div class="md-search-result" data-md-component="search-result">
185
185
  <div class="md-search-result__meta">
186
186
  Initializing search
@@ -218,7 +218,7 @@
218
218
 
219
219
 
220
220
  <li class="md-tabs__item">
221
- <a href="/projects/core/en/stable/index.html" class="md-tabs__link">
221
+ <a href="/projects/core/en/stable/overview/index.html" class="md-tabs__link">
222
222
 
223
223
 
224
224
 
@@ -343,7 +343,7 @@
343
343
 
344
344
  <nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
345
345
  <label class="md-nav__title" for="__drawer">
346
- <a href="/projects/core/en/stable/index.html" title="Nautobot Documentation" class="md-nav__button md-logo" aria-label="Nautobot Documentation" data-md-component="logo">
346
+ <a href="/projects/core/en/stable/." title="Nautobot Documentation" class="md-nav__button md-logo" aria-label="Nautobot Documentation" data-md-component="logo">
347
347
 
348
348
  <img src="/projects/core/en/stable/assets/nautobot_logo.svg" alt="logo">
349
349
 
@@ -371,19 +371,94 @@
371
371
 
372
372
 
373
373
 
374
- <li class="md-nav__item">
375
- <a href="/projects/core/en/stable/index.html" class="md-nav__link">
374
+
375
+
376
+
377
+
378
+
379
+
380
+
381
+ <li class="md-nav__item md-nav__item--nested">
382
+
383
+
384
+
385
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_1" >
376
386
 
387
+
388
+
389
+ <div class="md-nav__link md-nav__container">
390
+ <a href="/projects/core/en/stable/overview/index.html" class="md-nav__link ">
391
+
377
392
 
378
393
  <span class="md-ellipsis">
379
394
  Overview
380
395
  </span>
381
396
 
382
397
 
398
+ </a>
399
+
400
+
401
+ <label class="md-nav__link " for="__nav_1" id="__nav_1_label" tabindex="0">
402
+ <span class="md-nav__icon md-icon"></span>
403
+ </label>
404
+
405
+ </div>
406
+
407
+ <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_1_label" aria-expanded="false">
408
+ <label class="md-nav__title" for="__nav_1">
409
+ <span class="md-nav__icon md-icon"></span>
410
+ Overview
411
+ </label>
412
+ <ul class="md-nav__list" data-md-scrollfix>
413
+
414
+
415
+
416
+
417
+
418
+
419
+
420
+ <li class="md-nav__item">
421
+ <a href="/projects/core/en/stable/overview/design_philosophy.html" class="md-nav__link">
422
+
423
+
424
+ <span class="md-ellipsis">
425
+ Design Philosophy
426
+ </span>
427
+
428
+
429
+ </a>
430
+ </li>
431
+
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+ <li class="md-nav__item">
442
+ <a href="/projects/core/en/stable/overview/application_stack.html" class="md-nav__link">
443
+
444
+
445
+ <span class="md-ellipsis">
446
+ Application Stack
447
+ </span>
448
+
449
+
383
450
  </a>
384
451
  </li>
385
452
 
386
453
 
454
+
455
+
456
+ </ul>
457
+ </nav>
458
+
459
+ </li>
460
+
461
+
387
462
 
388
463
 
389
464
 
@@ -7838,7 +7913,7 @@
7838
7913
  <script id="__config" type="application/json">{"base": "/projects/core/en/stable/", "features": ["content.code.copy", "content.tabs.link", "navigation.footer", "navigation.tabs", "navigation.tabs.sticky", "navigation.tracking", "search.highlight", "search.share", "search.suggest"], "search": "/projects/core/en/stable/assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
7839
7914
 
7840
7915
 
7841
- <script src="/projects/core/en/stable/assets/javascripts/bundle.ebd0bdb7.min.js"></script>
7916
+ <script src="/projects/core/en/stable/assets/javascripts/bundle.fe8b6f2b.min.js"></script>
7842
7917
 
7843
7918
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
7844
7919