nautobot 2.4.0b1__py3-none-any.whl → 2.4.2__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 (668) hide show
  1. nautobot/apps/__init__.py +1 -1
  2. nautobot/apps/api.py +8 -8
  3. nautobot/apps/change_logging.py +2 -2
  4. nautobot/apps/choices.py +4 -4
  5. nautobot/apps/events.py +3 -3
  6. nautobot/apps/factory.py +2 -2
  7. nautobot/apps/filters.py +1 -1
  8. nautobot/apps/forms.py +20 -20
  9. nautobot/apps/graphql.py +2 -2
  10. nautobot/apps/jobs.py +8 -8
  11. nautobot/apps/models.py +19 -19
  12. nautobot/apps/tables.py +1 -1
  13. nautobot/apps/testing.py +10 -10
  14. nautobot/apps/ui.py +2 -2
  15. nautobot/apps/utils.py +7 -7
  16. nautobot/apps/views.py +7 -7
  17. nautobot/circuits/api/serializers.py +1 -0
  18. nautobot/circuits/api/views.py +4 -8
  19. nautobot/circuits/tables.py +2 -1
  20. nautobot/circuits/templates/circuits/circuit_create.html +1 -7
  21. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +43 -0
  22. nautobot/circuits/tests/integration/test_relationships.py +1 -1
  23. nautobot/circuits/views.py +3 -3
  24. nautobot/cloud/api/views.py +6 -10
  25. nautobot/cloud/models.py +1 -1
  26. nautobot/cloud/views.py +0 -16
  27. nautobot/core/api/constants.py +11 -0
  28. nautobot/core/api/fields.py +5 -5
  29. nautobot/core/api/filter_backends.py +3 -9
  30. nautobot/core/api/schema.py +13 -2
  31. nautobot/core/api/serializers.py +40 -34
  32. nautobot/core/api/views.py +56 -4
  33. nautobot/core/apps/__init__.py +0 -5
  34. nautobot/core/celery/log.py +4 -4
  35. nautobot/core/celery/schedulers.py +2 -2
  36. nautobot/core/choices.py +2 -2
  37. nautobot/core/events/__init__.py +3 -3
  38. nautobot/core/filters.py +67 -35
  39. nautobot/core/forms/__init__.py +19 -19
  40. nautobot/core/forms/fields.py +14 -11
  41. nautobot/core/forms/forms.py +33 -2
  42. nautobot/core/graphql/types.py +1 -1
  43. nautobot/core/jobs/__init__.py +28 -7
  44. nautobot/core/jobs/bulk_actions.py +285 -0
  45. nautobot/core/jobs/cleanup.py +48 -12
  46. nautobot/core/jobs/groups.py +1 -1
  47. nautobot/core/management/commands/validate_models.py +1 -1
  48. nautobot/core/models/__init__.py +3 -1
  49. nautobot/core/models/query_functions.py +2 -2
  50. nautobot/core/models/tree_queries.py +6 -3
  51. nautobot/core/settings.py +29 -2
  52. nautobot/core/settings.yaml +21 -0
  53. nautobot/core/tables.py +79 -61
  54. nautobot/core/templates/about.html +67 -0
  55. nautobot/core/templates/generic/object_bulk_destroy.html +1 -1
  56. nautobot/core/templates/inc/media.html +3 -0
  57. nautobot/core/templates/inc/nav_menu.html +1 -0
  58. nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
  59. nautobot/core/templates/inc/tenant_table_row.html +11 -0
  60. nautobot/core/templates/nautobot_config.py.j2 +13 -0
  61. nautobot/core/templates/search.html +7 -0
  62. nautobot/core/templates/utilities/render_jinja2.html +1 -1
  63. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  64. nautobot/core/templates/utilities/theme_preview.html +7 -0
  65. nautobot/core/templatetags/helpers.py +11 -2
  66. nautobot/core/testing/__init__.py +8 -8
  67. nautobot/core/testing/api.py +170 -15
  68. nautobot/core/testing/filters.py +45 -10
  69. nautobot/core/testing/forms.py +2 -0
  70. nautobot/core/testing/integration.py +514 -5
  71. nautobot/core/testing/mixins.py +7 -2
  72. nautobot/core/testing/views.py +44 -29
  73. nautobot/core/tests/integration/test_app_home.py +0 -1
  74. nautobot/core/tests/integration/test_app_navbar.py +0 -1
  75. nautobot/core/tests/integration/test_filters.py +0 -2
  76. nautobot/core/tests/integration/test_home.py +0 -1
  77. nautobot/core/tests/integration/test_navbar.py +0 -1
  78. nautobot/core/tests/integration/test_view_authentication.py +1 -0
  79. nautobot/core/tests/runner.py +1 -1
  80. nautobot/core/tests/test_api.py +98 -1
  81. nautobot/core/tests/test_csv.py +25 -3
  82. nautobot/core/tests/test_filters.py +209 -246
  83. nautobot/core/tests/test_forms.py +1 -0
  84. nautobot/core/tests/test_jobs.py +492 -1
  85. nautobot/core/tests/test_models.py +9 -0
  86. nautobot/core/tests/test_settings_schema.py +7 -0
  87. nautobot/core/tests/test_tables.py +100 -0
  88. nautobot/core/tests/test_utils.py +63 -1
  89. nautobot/core/tests/test_views.py +30 -3
  90. nautobot/core/ui/nav.py +1 -0
  91. nautobot/core/ui/object_detail.py +15 -1
  92. nautobot/core/urls.py +11 -0
  93. nautobot/core/utils/git.py +7 -2
  94. nautobot/core/utils/lookup.py +11 -8
  95. nautobot/core/utils/querysets.py +64 -0
  96. nautobot/core/utils/requests.py +24 -9
  97. nautobot/core/views/__init__.py +42 -0
  98. nautobot/core/views/generic.py +131 -197
  99. nautobot/core/views/mixins.py +136 -41
  100. nautobot/core/views/renderers.py +6 -6
  101. nautobot/core/views/utils.py +2 -2
  102. nautobot/dcim/api/serializers.py +56 -64
  103. nautobot/dcim/api/views.py +47 -113
  104. nautobot/dcim/constants.py +6 -13
  105. nautobot/dcim/factory.py +6 -1
  106. nautobot/dcim/filters/__init__.py +31 -2
  107. nautobot/dcim/forms.py +48 -17
  108. nautobot/dcim/graphql/types.py +2 -2
  109. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
  110. nautobot/dcim/models/__init__.py +1 -1
  111. nautobot/dcim/models/device_component_templates.py +2 -2
  112. nautobot/dcim/models/device_components.py +22 -20
  113. nautobot/dcim/models/devices.py +10 -1
  114. nautobot/dcim/models/locations.py +3 -3
  115. nautobot/dcim/models/power.py +6 -5
  116. nautobot/dcim/models/racks.py +4 -4
  117. nautobot/dcim/tables/__init__.py +3 -3
  118. nautobot/dcim/tables/devices.py +9 -6
  119. nautobot/dcim/tables/devicetypes.py +2 -2
  120. nautobot/dcim/tables/racks.py +1 -1
  121. nautobot/dcim/templates/dcim/cable.html +1 -1
  122. nautobot/dcim/templates/dcim/controller_create.html +1 -7
  123. nautobot/dcim/templates/dcim/controller_retrieve.html +1 -9
  124. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +2 -0
  125. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +5 -0
  126. nautobot/dcim/templates/dcim/device/base.html +1 -1
  127. nautobot/dcim/templates/dcim/device.html +3 -11
  128. nautobot/dcim/templates/dcim/device_component.html +1 -1
  129. nautobot/dcim/templates/dcim/device_edit.html +36 -37
  130. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  131. nautobot/dcim/templates/dcim/location.html +2 -10
  132. nautobot/dcim/templates/dcim/location_edit.html +1 -7
  133. nautobot/dcim/templates/dcim/locationtype.html +1 -1
  134. nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
  135. nautobot/dcim/templates/dcim/manufacturer.html +1 -1
  136. nautobot/dcim/templates/dcim/platform.html +1 -1
  137. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  138. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  139. nautobot/dcim/templates/dcim/rack.html +2 -10
  140. nautobot/dcim/templates/dcim/rack_edit.html +1 -7
  141. nautobot/dcim/templates/dcim/rackgroup.html +1 -1
  142. nautobot/dcim/templates/dcim/rackreservation.html +3 -11
  143. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  144. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -9
  145. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +1 -7
  146. nautobot/dcim/tests/integration/test_controller.py +62 -0
  147. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
  148. nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
  149. nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
  150. nautobot/dcim/tests/test_api.py +16 -5
  151. nautobot/dcim/tests/test_filters.py +33 -0
  152. nautobot/dcim/tests/test_forms.py +51 -2
  153. nautobot/dcim/tests/test_graphql.py +52 -0
  154. nautobot/dcim/tests/test_jobs.py +118 -0
  155. nautobot/dcim/tests/test_models.py +52 -9
  156. nautobot/dcim/tests/test_views.py +30 -84
  157. nautobot/dcim/views.py +13 -28
  158. nautobot/extras/api/customfields.py +2 -2
  159. nautobot/extras/api/serializers.py +123 -85
  160. nautobot/extras/api/views.py +33 -30
  161. nautobot/extras/constants.py +3 -0
  162. nautobot/extras/datasources/git.py +125 -0
  163. nautobot/extras/filters/__init__.py +8 -6
  164. nautobot/extras/forms/base.py +2 -2
  165. nautobot/extras/forms/forms.py +139 -31
  166. nautobot/extras/forms/mixins.py +14 -6
  167. nautobot/extras/group_sync.py +3 -3
  168. nautobot/extras/health_checks.py +1 -2
  169. nautobot/extras/jobs.py +85 -18
  170. nautobot/extras/managers.py +3 -1
  171. nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
  172. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
  173. nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
  174. nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
  175. nautobot/extras/models/__init__.py +1 -1
  176. nautobot/extras/models/contacts.py +1 -1
  177. nautobot/extras/models/customfields.py +41 -23
  178. nautobot/extras/models/datasources.py +85 -0
  179. nautobot/extras/models/groups.py +11 -9
  180. nautobot/extras/models/jobs.py +23 -4
  181. nautobot/extras/models/models.py +17 -2
  182. nautobot/extras/models/relationships.py +17 -5
  183. nautobot/extras/plugins/__init__.py +13 -2
  184. nautobot/extras/plugins/marketplace_manifest.yml +84 -79
  185. nautobot/extras/plugins/tables.py +16 -14
  186. nautobot/extras/plugins/views.py +65 -69
  187. nautobot/extras/registry.py +1 -1
  188. nautobot/extras/secrets/__init__.py +2 -2
  189. nautobot/extras/signals.py +15 -1
  190. nautobot/extras/tables.py +7 -5
  191. nautobot/extras/templates/extras/computedfield.html +1 -1
  192. nautobot/extras/templates/extras/configcontext.html +1 -1
  193. nautobot/extras/templates/extras/configcontextschema.html +1 -1
  194. nautobot/extras/templates/extras/customfield.html +1 -1
  195. nautobot/extras/templates/extras/customlink.html +1 -1
  196. nautobot/extras/templates/extras/dynamicgroup.html +2 -10
  197. nautobot/extras/templates/extras/exporttemplate.html +1 -1
  198. nautobot/extras/templates/extras/gitrepository.html +1 -1
  199. nautobot/extras/templates/extras/graphqlquery.html +1 -1
  200. nautobot/extras/templates/extras/job_detail.html +17 -1
  201. nautobot/extras/templates/extras/job_edit.html +1 -0
  202. nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
  203. nautobot/extras/templates/extras/jobhook.html +1 -1
  204. nautobot/extras/templates/extras/jobqueue_retrieve.html +1 -9
  205. nautobot/extras/templates/extras/jobresult.html +1 -1
  206. nautobot/extras/templates/extras/marketplace.html +29 -11
  207. nautobot/extras/templates/extras/objectchange.html +1 -1
  208. nautobot/extras/templates/extras/plugin_detail.html +33 -16
  209. nautobot/extras/templates/extras/plugins_tiles.html +21 -10
  210. nautobot/extras/templates/extras/relationship.html +1 -63
  211. nautobot/extras/templates/extras/role_retrieve.html +1 -1
  212. nautobot/extras/templates/extras/scheduledjob.html +1 -1
  213. nautobot/extras/templates/extras/secret.html +1 -1
  214. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  215. nautobot/extras/templates/extras/status.html +1 -1
  216. nautobot/extras/templates/extras/tag.html +1 -1
  217. nautobot/extras/templates/extras/webhook.html +1 -1
  218. nautobot/extras/templatetags/job_buttons.py +4 -4
  219. nautobot/extras/test_jobs/api_test_job.py +1 -1
  220. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  221. nautobot/extras/test_jobs/dry_run.py +1 -1
  222. nautobot/extras/test_jobs/fail.py +5 -5
  223. nautobot/extras/test_jobs/file_output.py +1 -1
  224. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  225. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  226. nautobot/extras/test_jobs/ipaddress_vars.py +3 -1
  227. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  228. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  229. nautobot/extras/test_jobs/log_redaction.py +1 -1
  230. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  231. nautobot/extras/test_jobs/modify_db.py +1 -1
  232. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  233. nautobot/extras/test_jobs/object_var_required.py +1 -1
  234. nautobot/extras/test_jobs/object_vars.py +1 -1
  235. nautobot/extras/test_jobs/pass.py +3 -3
  236. nautobot/extras/test_jobs/profiling.py +1 -1
  237. nautobot/extras/test_jobs/relative_import.py +3 -3
  238. nautobot/extras/test_jobs/singleton.py +16 -0
  239. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  240. nautobot/extras/test_jobs/task_queues.py +1 -1
  241. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
  242. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
  243. nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
  244. nautobot/extras/tests/git_helper.py +9 -1
  245. nautobot/extras/tests/integration/__init__.py +29 -16
  246. nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
  247. nautobot/extras/tests/test_api.py +19 -13
  248. nautobot/extras/tests/test_customfields.py +50 -52
  249. nautobot/extras/tests/test_datasources.py +29 -1
  250. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  251. nautobot/extras/tests/test_filters.py +6 -6
  252. nautobot/extras/tests/test_forms.py +33 -1
  253. nautobot/extras/tests/test_jobs.py +178 -32
  254. nautobot/extras/tests/test_models.py +299 -10
  255. nautobot/extras/tests/test_plugins.py +62 -9
  256. nautobot/extras/tests/test_relationships.py +120 -9
  257. nautobot/extras/tests/test_utils.py +22 -1
  258. nautobot/extras/tests/test_views.py +56 -194
  259. nautobot/extras/utils.py +20 -10
  260. nautobot/extras/views.py +85 -110
  261. nautobot/ipam/api/fields.py +3 -3
  262. nautobot/ipam/api/serializers.py +41 -33
  263. nautobot/ipam/api/views.py +68 -117
  264. nautobot/ipam/factory.py +1 -1
  265. nautobot/ipam/filters.py +3 -2
  266. nautobot/ipam/lookups.py +101 -62
  267. nautobot/ipam/models.py +74 -18
  268. nautobot/ipam/querysets.py +2 -2
  269. nautobot/ipam/tables.py +25 -9
  270. nautobot/ipam/templates/ipam/ipaddress.html +2 -10
  271. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
  272. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
  273. nautobot/ipam/templates/ipam/prefix.html +2 -10
  274. nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
  275. nautobot/ipam/templates/ipam/rir.html +1 -1
  276. nautobot/ipam/templates/ipam/routetarget.html +1 -1
  277. nautobot/ipam/templates/ipam/service.html +1 -1
  278. nautobot/ipam/templates/ipam/vlan.html +2 -10
  279. nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
  280. nautobot/ipam/templates/ipam/vlangroup.html +1 -1
  281. nautobot/ipam/templates/ipam/vrf.html +1 -1
  282. nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
  283. nautobot/ipam/tests/test_api.py +436 -3
  284. nautobot/ipam/tests/test_forms.py +49 -47
  285. nautobot/ipam/tests/test_migrations.py +30 -30
  286. nautobot/ipam/tests/test_models.py +119 -34
  287. nautobot/ipam/tests/test_querysets.py +63 -1
  288. nautobot/ipam/tests/test_utils.py +41 -2
  289. nautobot/ipam/tests/test_views.py +3 -0
  290. nautobot/ipam/utils/__init__.py +54 -17
  291. nautobot/ipam/views.py +61 -87
  292. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  293. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  294. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
  295. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  296. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  297. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  298. nautobot/project-static/docs/404.html +131 -14
  299. nautobot/project-static/docs/apps/index.html +131 -14
  300. nautobot/project-static/docs/apps/nautobot-apps.html +132 -16
  301. nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
  302. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.60a45f97.min.js} +2 -2
  303. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.60a45f97.min.js.map} +2 -2
  304. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
  305. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
  306. nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
  307. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +147 -20
  308. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +144 -17
  309. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +459 -132
  310. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +175 -28
  311. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +180 -31
  312. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +138 -16
  313. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +137 -15
  314. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +164 -27
  315. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +187 -38
  316. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +193 -31
  317. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +216 -48
  318. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +324 -75
  319. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +666 -175
  320. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +194 -46
  321. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +538 -177
  322. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +578 -221
  323. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +145 -18
  324. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +156 -25
  325. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +492 -65
  326. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +705 -215
  327. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +943 -422
  328. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +144 -17
  329. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +619 -200
  330. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +474 -159
  331. nautobot/project-static/docs/development/apps/api/configuration-view.html +131 -14
  332. nautobot/project-static/docs/development/apps/api/database-backend-config.html +131 -14
  333. nautobot/project-static/docs/development/apps/api/models/django-admin.html +131 -14
  334. nautobot/project-static/docs/development/apps/api/models/global-search.html +131 -14
  335. nautobot/project-static/docs/development/apps/api/models/graphql.html +131 -14
  336. nautobot/project-static/docs/development/apps/api/models/index.html +131 -14
  337. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +131 -14
  338. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +131 -14
  339. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +131 -14
  340. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +131 -14
  341. nautobot/project-static/docs/development/apps/api/platform-features/index.html +131 -14
  342. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +131 -14
  343. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +131 -14
  344. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +131 -14
  345. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +131 -14
  346. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +153 -17
  347. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +131 -14
  348. nautobot/project-static/docs/development/apps/api/prometheus.html +131 -14
  349. nautobot/project-static/docs/development/apps/api/setup.html +131 -14
  350. nautobot/project-static/docs/development/apps/api/testing.html +131 -14
  351. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +131 -14
  352. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +131 -14
  353. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +131 -14
  354. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +131 -14
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +131 -14
  356. nautobot/project-static/docs/development/apps/api/views/base-template.html +131 -14
  357. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +131 -14
  358. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +131 -14
  359. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +131 -14
  360. nautobot/project-static/docs/development/apps/api/views/index.html +131 -14
  361. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +131 -14
  362. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +131 -14
  363. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +131 -14
  364. nautobot/project-static/docs/development/apps/api/views/notes.html +131 -14
  365. nautobot/project-static/docs/development/apps/api/views/rest-api.html +137 -16
  366. nautobot/project-static/docs/development/apps/api/views/urls.html +131 -14
  367. nautobot/project-static/docs/development/apps/index.html +131 -14
  368. nautobot/project-static/docs/development/apps/migration/code-updates.html +131 -14
  369. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +131 -14
  370. nautobot/project-static/docs/development/apps/migration/from-v1.html +131 -14
  371. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +131 -14
  372. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +131 -14
  373. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +131 -14
  374. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +131 -14
  375. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +135 -18
  376. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +131 -14
  377. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +297 -25
  378. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +131 -14
  379. nautobot/project-static/docs/development/apps/porting-from-netbox.html +131 -14
  380. nautobot/project-static/docs/development/core/application-registry.html +131 -14
  381. nautobot/project-static/docs/development/core/best-practices.html +131 -14
  382. nautobot/project-static/docs/development/core/bootstrap-ui.html +131 -14
  383. nautobot/project-static/docs/development/core/caching.html +131 -14
  384. nautobot/project-static/docs/development/core/controllers.html +131 -14
  385. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +158 -84
  386. nautobot/project-static/docs/development/core/generic-views.html +131 -14
  387. nautobot/project-static/docs/development/core/getting-started.html +334 -234
  388. nautobot/project-static/docs/development/core/homepage.html +134 -17
  389. nautobot/project-static/docs/development/core/index.html +131 -14
  390. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9829 -0
  391. nautobot/project-static/docs/development/core/model-checklist.html +141 -22
  392. nautobot/project-static/docs/development/core/model-features.html +131 -14
  393. nautobot/project-static/docs/development/core/natural-keys.html +131 -14
  394. nautobot/project-static/docs/development/core/navigation-menu.html +131 -14
  395. nautobot/project-static/docs/development/core/release-checklist.html +134 -17
  396. nautobot/project-static/docs/development/core/role-internals.html +131 -14
  397. nautobot/project-static/docs/development/core/settings.html +131 -14
  398. nautobot/project-static/docs/development/core/style-guide.html +134 -17
  399. nautobot/project-static/docs/development/core/templates.html +132 -15
  400. nautobot/project-static/docs/development/core/testing.html +131 -14
  401. nautobot/project-static/docs/development/core/ui-component-framework.html +454 -283
  402. nautobot/project-static/docs/development/core/user-preferences.html +131 -14
  403. nautobot/project-static/docs/development/index.html +131 -14
  404. nautobot/project-static/docs/development/jobs/index.html +301 -132
  405. nautobot/project-static/docs/development/jobs/migration/from-v1.html +131 -14
  406. nautobot/project-static/docs/index.html +139 -33
  407. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  408. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  409. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  410. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  411. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  412. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  413. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  414. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  415. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  416. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  417. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  418. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  419. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  420. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  421. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  422. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  423. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  424. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  425. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  426. nautobot/project-static/docs/objects.inv +0 -0
  427. nautobot/project-static/docs/overview/application_stack.html +132 -17
  428. nautobot/project-static/docs/overview/design_philosophy.html +131 -14
  429. nautobot/project-static/docs/release-notes/index.html +137 -22
  430. nautobot/project-static/docs/release-notes/version-1.0.html +319 -203
  431. nautobot/project-static/docs/release-notes/version-1.1.html +316 -200
  432. nautobot/project-static/docs/release-notes/version-1.2.html +391 -275
  433. nautobot/project-static/docs/release-notes/version-1.3.html +417 -301
  434. nautobot/project-static/docs/release-notes/version-1.4.html +502 -387
  435. nautobot/project-static/docs/release-notes/version-1.5.html +690 -576
  436. nautobot/project-static/docs/release-notes/version-1.6.html +989 -457
  437. nautobot/project-static/docs/release-notes/version-2.0.html +613 -499
  438. nautobot/project-static/docs/release-notes/version-2.1.html +448 -334
  439. nautobot/project-static/docs/release-notes/version-2.2.html +441 -327
  440. nautobot/project-static/docs/release-notes/version-2.3.html +1171 -451
  441. nautobot/project-static/docs/release-notes/version-2.4.html +800 -111
  442. nautobot/project-static/docs/requirements.txt +2 -2
  443. nautobot/project-static/docs/search/search_index.json +1 -1
  444. nautobot/project-static/docs/sitemap.xml +303 -287
  445. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  446. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +131 -14
  447. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +131 -14
  448. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +133 -16
  449. nautobot/project-static/docs/user-guide/administration/configuration/index.html +131 -14
  450. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +131 -14
  451. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +195 -18
  452. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +131 -14
  453. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +134 -17
  454. nautobot/project-static/docs/user-guide/administration/guides/docker.html +131 -14
  455. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +131 -14
  456. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +131 -14
  457. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +131 -14
  458. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +131 -14
  459. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +131 -14
  460. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +133 -16
  461. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +131 -14
  462. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +131 -14
  463. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +131 -14
  464. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +151 -18
  465. nautobot/project-static/docs/user-guide/administration/installation/index.html +131 -14
  466. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +132 -15
  467. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +131 -14
  468. nautobot/project-static/docs/user-guide/administration/installation/services.html +131 -14
  469. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +131 -14
  470. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +131 -14
  471. nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
  472. nautobot/project-static/docs/user-guide/administration/security/notices.html +9843 -0
  473. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +131 -14
  474. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +131 -14
  475. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +131 -14
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +131 -14
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +131 -14
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +131 -14
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +131 -14
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +134 -18
  481. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +131 -14
  482. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +131 -14
  483. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +131 -14
  484. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +131 -14
  485. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +131 -14
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +131 -14
  487. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +131 -14
  488. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +131 -14
  489. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +131 -14
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +131 -14
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +131 -14
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +131 -14
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +131 -14
  494. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +131 -14
  495. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +131 -14
  496. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +131 -14
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +131 -14
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +131 -14
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +131 -14
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +131 -14
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +131 -14
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +131 -14
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +131 -14
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +131 -14
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +131 -14
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +131 -14
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +131 -14
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +131 -14
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +131 -14
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +131 -14
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +131 -14
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +131 -14
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +131 -14
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +131 -14
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +131 -14
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +131 -14
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +131 -14
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +131 -14
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +131 -14
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +131 -14
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +131 -14
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +131 -14
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +131 -14
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +131 -14
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +131 -14
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +131 -14
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +131 -14
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +131 -14
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +131 -14
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +131 -14
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +131 -14
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +131 -14
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +131 -14
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +131 -14
  535. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +131 -14
  536. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +131 -14
  537. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +131 -14
  538. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +135 -22
  539. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +131 -14
  540. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +131 -14
  541. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +131 -14
  542. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +131 -14
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +131 -14
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +131 -14
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +131 -14
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +131 -14
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +131 -14
  548. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +131 -14
  549. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +131 -14
  550. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +131 -14
  551. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +134 -17
  552. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +131 -14
  553. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +131 -14
  554. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +131 -14
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +131 -14
  556. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +131 -14
  557. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +131 -14
  558. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +131 -14
  559. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +131 -14
  560. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +131 -14
  561. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +131 -14
  562. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +131 -14
  563. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +131 -14
  564. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +131 -14
  565. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +131 -14
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +131 -14
  567. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +134 -17
  568. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +131 -14
  569. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +131 -14
  570. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +131 -14
  571. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +131 -14
  572. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +131 -14
  573. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +131 -14
  574. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +236 -34
  575. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +131 -14
  576. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  587. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  589. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +131 -14
  590. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +131 -14
  591. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +134 -17
  592. nautobot/project-static/docs/{development/core/local-k8s.html → user-guide/feature-guides/wireless-networks-and-controllers.html} +632 -566
  593. nautobot/project-static/docs/user-guide/index.html +131 -14
  594. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +131 -14
  595. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +135 -18
  596. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +131 -14
  597. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +131 -14
  598. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +131 -14
  599. nautobot/project-static/docs/user-guide/platform-functionality/events.html +131 -14
  600. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +131 -14
  601. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +131 -14
  602. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +451 -16
  603. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +131 -14
  604. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +131 -14
  605. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +131 -14
  606. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +135 -17
  607. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +131 -14
  608. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +134 -17
  609. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +131 -14
  610. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +131 -14
  611. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9797 -0
  612. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +131 -14
  613. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +131 -14
  614. nautobot/project-static/docs/user-guide/platform-functionality/note.html +131 -14
  615. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +131 -14
  616. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +132 -15
  617. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +131 -14
  618. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +131 -14
  619. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +179 -35
  620. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +159 -15
  621. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +131 -14
  622. nautobot/project-static/docs/user-guide/platform-functionality/role.html +131 -14
  623. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +131 -14
  624. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +131 -14
  625. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +131 -14
  626. nautobot/project-static/docs/user-guide/platform-functionality/status.html +131 -14
  627. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +131 -14
  628. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +131 -14
  629. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +131 -14
  630. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +131 -14
  631. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +131 -14
  632. nautobot/project-static/js/forms.js +1 -1
  633. nautobot/tenancy/api/views.py +9 -13
  634. nautobot/tenancy/templates/tenancy/tenant.html +1 -2
  635. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  636. nautobot/tenancy/views.py +4 -2
  637. nautobot/users/admin.py +1 -1
  638. nautobot/users/api/serializers.py +5 -4
  639. nautobot/users/api/views.py +3 -3
  640. nautobot/virtualization/api/serializers.py +4 -4
  641. nautobot/virtualization/api/views.py +5 -24
  642. nautobot/virtualization/filters.py +20 -3
  643. nautobot/virtualization/models.py +1 -1
  644. nautobot/virtualization/tables.py +2 -2
  645. nautobot/virtualization/templates/virtualization/cluster.html +1 -1
  646. nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
  647. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
  648. nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
  649. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -10
  650. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
  651. nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
  652. nautobot/virtualization/tests/test_filters.py +17 -0
  653. nautobot/wireless/filters.py +2 -2
  654. nautobot/wireless/forms.py +1 -1
  655. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +1 -9
  656. nautobot/wireless/tests/integration/__init__.py +0 -0
  657. nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
  658. nautobot/wireless/tests/test_filters.py +29 -1
  659. nautobot/wireless/tests/test_views.py +22 -1
  660. nautobot/wireless/views.py +0 -10
  661. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/METADATA +9 -9
  662. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/RECORD +667 -610
  663. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/WHEEL +1 -1
  664. nautobot/core/fixtures/user-data.json +0 -59
  665. /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
  666. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/LICENSE.txt +0 -0
  667. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/NOTICE +0 -0
  668. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  """Test IPAM forms."""
2
2
 
3
+ from django.forms import Form
3
4
  from django.test import TestCase
4
5
 
5
6
  from nautobot.core.testing.forms import FormTestCases
@@ -9,62 +10,63 @@ from nautobot.ipam.choices import IPAddressTypeChoices
9
10
  from nautobot.ipam.models import IPAddress, Namespace, Prefix
10
11
 
11
12
 
12
- class BaseNetworkFormTest:
13
- form_class = None
14
- field_name = None
15
- object_name = None
16
- extra_data = {}
17
-
18
- def setUp(self):
19
- super().setUp()
20
- self.namespace = Namespace.objects.create(name="IPAM Form Test")
21
- self.status = Status.objects.get(name="Active")
22
- self.prefix_status = Status.objects.get_for_model(Prefix).first()
23
- self.ip_status = Status.objects.get_for_model(IPAddress).first()
24
- self.parent = Prefix.objects.create(
25
- prefix="192.168.1.0/24", namespace=self.namespace, status=self.prefix_status
26
- )
27
- self.parent2 = Prefix.objects.create(
28
- prefix="192.168.0.0/16", namespace=self.namespace, status=self.prefix_status
29
- )
30
- self.parent6 = Prefix.objects.create(
31
- prefix="2001:0db8::/40", namespace=self.namespace, status=self.prefix_status
32
- )
13
+ class NetworkFormTestCases:
14
+ class BaseNetworkFormTest(TestCase):
15
+ form_class: type[Form]
16
+ field_name: str
17
+ object_name: str
18
+ extra_data = {}
19
+
20
+ def setUp(self):
21
+ super().setUp()
22
+ self.namespace = Namespace.objects.create(name="IPAM Form Test")
23
+ self.status = Status.objects.get(name="Active")
24
+ self.prefix_status = Status.objects.get_for_model(Prefix).first()
25
+ self.ip_status = Status.objects.get_for_model(IPAddress).first()
26
+ self.parent = Prefix.objects.create(
27
+ prefix="192.168.1.0/24", namespace=self.namespace, status=self.prefix_status
28
+ )
29
+ self.parent2 = Prefix.objects.create(
30
+ prefix="192.168.0.0/16", namespace=self.namespace, status=self.prefix_status
31
+ )
32
+ self.parent6 = Prefix.objects.create(
33
+ prefix="2001:0db8::/40", namespace=self.namespace, status=self.prefix_status
34
+ )
33
35
 
34
- def test_valid_ip_address(self):
35
- data = {self.field_name: "192.168.2.0/24", "namespace": self.namespace, "status": self.status}
36
- data.update(self.extra_data)
37
- form = self.form_class(data)
36
+ def test_valid_ip_address(self):
37
+ data = {self.field_name: "192.168.2.0/24", "namespace": self.namespace, "status": self.status}
38
+ data.update(self.extra_data)
39
+ form = self.form_class(data)
38
40
 
39
- self.assertTrue(form.is_valid())
40
- self.assertTrue(form.save())
41
+ self.assertTrue(form.is_valid())
42
+ self.assertTrue(form.save())
41
43
 
42
- def test_address_invalid_ipv4(self):
43
- data = {self.field_name: "192.168.0.1/64", "namespace": self.namespace, "status": self.status}
44
- data.update(self.extra_data)
45
- form = self.form_class(data)
44
+ def test_address_invalid_ipv4(self):
45
+ data = {self.field_name: "192.168.0.1/64", "namespace": self.namespace, "status": self.status}
46
+ data.update(self.extra_data)
47
+ form = self.form_class(data)
46
48
 
47
- self.assertFalse(form.is_valid())
48
- self.assertEqual("Please specify a valid IPv4 or IPv6 address.", form.errors[self.field_name][0])
49
+ self.assertFalse(form.is_valid())
50
+ self.assertEqual("Please specify a valid IPv4 or IPv6 address.", form.errors[self.field_name][0])
49
51
 
50
- def test_address_zero_mask(self):
51
- data = {self.field_name: "192.168.0.1/0", "namespace": self.namespace, "status": self.status}
52
- data.update(self.extra_data)
53
- form = self.form_class(data)
52
+ def test_address_zero_mask(self):
53
+ data = {self.field_name: "192.168.0.1/0", "namespace": self.namespace, "status": self.status}
54
+ data.update(self.extra_data)
55
+ form = self.form_class(data)
54
56
 
55
- # With the advent of `Prefix.parent`, it's now possible to create a /0 .
56
- self.assertTrue(form.is_valid())
57
+ # With the advent of `Prefix.parent`, it's now possible to create a /0 .
58
+ self.assertTrue(form.is_valid())
57
59
 
58
- def test_address_missing_mask(self):
59
- data = {self.field_name: "192.168.0.1", "namespace": self.namespace, "status": self.status}
60
- data.update(self.extra_data)
61
- form = self.form_class(data)
60
+ def test_address_missing_mask(self):
61
+ data = {self.field_name: "192.168.0.1", "namespace": self.namespace, "status": self.status}
62
+ data.update(self.extra_data)
63
+ form = self.form_class(data)
62
64
 
63
- self.assertFalse(form.is_valid())
64
- self.assertEqual("CIDR mask (e.g. /24) is required.", form.errors[self.field_name][0])
65
+ self.assertFalse(form.is_valid())
66
+ self.assertEqual("CIDR mask (e.g. /24) is required.", form.errors[self.field_name][0])
65
67
 
66
68
 
67
- class PrefixFormTest(BaseNetworkFormTest, FormTestCases.BaseFormTestCase):
69
+ class PrefixFormTest(NetworkFormTestCases.BaseNetworkFormTest, FormTestCases.BaseFormTestCase):
68
70
  form_class = forms.PrefixForm
69
71
  field_name = "prefix"
70
72
  object_name = "prefix"
@@ -79,7 +81,7 @@ class PrefixFormTest(BaseNetworkFormTest, FormTestCases.BaseFormTestCase):
79
81
  }
80
82
 
81
83
 
82
- class IPAddressFormTest(BaseNetworkFormTest, TestCase):
84
+ class IPAddressFormTest(NetworkFormTestCases.BaseNetworkFormTest):
83
85
  form_class = forms.IPAddressForm
84
86
  field_name = "address"
85
87
  object_name = "IP address"
@@ -29,29 +29,29 @@ class AggregateToPrefixMigrationTestCase(NautobotDataMigrationTest):
29
29
  request_id=uuid.uuid4(),
30
30
  )
31
31
 
32
- def populateDataBeforeMigration(self, apps):
32
+ def populateDataBeforeMigration(self, installed_apps):
33
33
  """Populate Aggregate data before migrating to Prefixes"""
34
34
 
35
- self.aggregate = apps.get_model("ipam", "Aggregate")
35
+ self.aggregate = installed_apps.get_model("ipam", "Aggregate")
36
36
  # Workaround for django-taggit manager not working in migrations.
37
37
  # https://github.com/jazzband/django-taggit/issues/101
38
38
  # https://github.com/jazzband/django-taggit/issues/454
39
39
  self.aggregate.tags = TagsField()
40
- self.computed_field = apps.get_model("extras", "computedfield")
41
- self.content_type = apps.get_model("contenttypes", "ContentType")
42
- self.custom_field = apps.get_model("extras", "customfield")
43
- self.custom_link = apps.get_model("extras", "customlink")
44
- self.dynamic_group = apps.get_model("extras", "DynamicGroup")
45
- self.note = apps.get_model("extras", "note")
46
- self.object_change = apps.get_model("extras", "objectchange")
47
- self.object_permission = apps.get_model("users", "objectpermission")
48
- self.prefix = apps.get_model("ipam", "prefix")
40
+ self.computed_field = installed_apps.get_model("extras", "computedfield")
41
+ self.content_type = installed_apps.get_model("contenttypes", "ContentType")
42
+ self.custom_field = installed_apps.get_model("extras", "customfield")
43
+ self.custom_link = installed_apps.get_model("extras", "customlink")
44
+ self.dynamic_group = installed_apps.get_model("extras", "DynamicGroup")
45
+ self.note = installed_apps.get_model("extras", "note")
46
+ self.object_change = installed_apps.get_model("extras", "objectchange")
47
+ self.object_permission = installed_apps.get_model("users", "objectpermission")
48
+ self.prefix = installed_apps.get_model("ipam", "prefix")
49
49
  self.prefix.tags = TagsField()
50
- self.relationship = apps.get_model("extras", "relationship")
51
- self.relationship_association = apps.get_model("extras", "relationshipassociation")
52
- self.rir = apps.get_model("ipam", "RIR")
53
- self.status = apps.get_model("extras", "status")
54
- self.tag = apps.get_model("extras", "tag")
50
+ self.relationship = installed_apps.get_model("extras", "relationship")
51
+ self.relationship_association = installed_apps.get_model("extras", "relationshipassociation")
52
+ self.rir = installed_apps.get_model("ipam", "RIR")
53
+ self.status = installed_apps.get_model("extras", "status")
54
+ self.tag = installed_apps.get_model("extras", "tag")
55
55
 
56
56
  self.aggregate_ct = self.content_type.objects.get_for_model(self.aggregate)
57
57
  self.prefix_ct = self.content_type.objects.get_for_model(self.prefix)
@@ -114,8 +114,8 @@ class AggregateToPrefixMigrationTestCase(NautobotDataMigrationTest):
114
114
  self.aggregate1.tags.add("AggregateTagA", "AggregateTagB")
115
115
  self.aggregate2.tags.add("AggregateTagA")
116
116
  self.aggregate3.tags.add("AggregateTagB")
117
- self.aggregate5.tags.add("AggregateTagA", "AggregateTagB")
118
- self.aggregate6.tags.add("AggregateTagB")
117
+ self.aggregate5.tags.add("AggregateTagA", "AggregateTagB") # pylint: disable=no-member
118
+ self.aggregate6.tags.add("AggregateTagB") # pylint: disable=no-member
119
119
 
120
120
  # notes
121
121
  self.note.objects.create(
@@ -141,7 +141,7 @@ class AggregateToPrefixMigrationTestCase(NautobotDataMigrationTest):
141
141
  self.note.objects.create(
142
142
  note="Aggregate5 test note",
143
143
  assigned_object_type=self.aggregate_ct,
144
- assigned_object_id=self.aggregate5.id,
144
+ assigned_object_id=self.aggregate5.id, # pylint: disable=no-member
145
145
  )
146
146
 
147
147
  # object permissions
@@ -157,8 +157,8 @@ class AggregateToPrefixMigrationTestCase(NautobotDataMigrationTest):
157
157
  # object changes
158
158
  self._create_objectchange(self.prefix1, "Pre-migration object change for prefix1")
159
159
  self._create_objectchange(self.prefix4, "Pre-migration object change for prefix4")
160
- self._create_objectchange(self.prefix5, "Pre-migration object change for prefix5")
161
- self._create_objectchange(self.aggregate5, "Pre-migration object change for aggregate5")
160
+ self._create_objectchange(self.prefix5, "Pre-migration object change for prefix5") # pylint: disable=no-member
161
+ self._create_objectchange(self.aggregate5, "Pre-migration object change for aggregate5") # pylint: disable=no-member
162
162
 
163
163
  # custom fields
164
164
  prefix_cf1 = self.custom_field.objects.create(name="prefixcf1")
@@ -179,24 +179,24 @@ class AggregateToPrefixMigrationTestCase(NautobotDataMigrationTest):
179
179
 
180
180
  self.aggregate3._custom_field_data["aggregatecf1"] = "testdata aggregatecf1 aggregate3"
181
181
 
182
- self.prefix5._custom_field_data["prefixcf1"] = "testdata prefixcf1 prefix5"
183
- self.prefix5._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 prefix5"
182
+ self.prefix5._custom_field_data["prefixcf1"] = "testdata prefixcf1 prefix5" # pylint: disable=no-member
183
+ self.prefix5._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 prefix5" # pylint: disable=no-member
184
184
 
185
- self.aggregate5._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 aggregate5"
186
- self.aggregate5._custom_field_data["aggregatecf1"] = "testdata aggregatecf1 aggregate5"
185
+ self.aggregate5._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 aggregate5" # pylint: disable=no-member
186
+ self.aggregate5._custom_field_data["aggregatecf1"] = "testdata aggregatecf1 aggregate5" # pylint: disable=no-member
187
187
 
188
- self.aggregate6._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 aggregate6"
188
+ self.aggregate6._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 aggregate6" # pylint: disable=no-member
189
189
 
190
190
  self.prefix1.save()
191
191
  self.prefix2.save()
192
192
  self.prefix3.save()
193
193
  self.prefix4.save()
194
- self.prefix5.save()
194
+ self.prefix5.save() # pylint: disable=no-member
195
195
  self.aggregate1.save()
196
196
  self.aggregate2.save()
197
197
  self.aggregate3.save()
198
- self.aggregate5.save()
199
- self.aggregate6.save()
198
+ self.aggregate5.save() # pylint: disable=no-member
199
+ self.aggregate6.save() # pylint: disable=no-member
200
200
 
201
201
  @skipIf(
202
202
  connection.vendor != "postgresql",
@@ -390,7 +390,7 @@ class AggregateToPrefixMigrationTestCase(NautobotDataMigrationTest):
390
390
  2,
391
391
  )
392
392
 
393
- for prefix in (self.prefix2, self.prefix3, self.prefix5):
393
+ for prefix in (self.prefix2, self.prefix3, self.prefix5): # pylint: disable=no-member
394
394
  self.assertEqual(
395
395
  self.object_change.objects.filter(changed_object_id=prefix.id).count(),
396
396
  1,
@@ -1,4 +1,5 @@
1
1
  from unittest import skipIf
2
+ from unittest.mock import patch
2
3
 
3
4
  from django.contrib.contenttypes.models import ContentType
4
5
  from django.core.exceptions import ValidationError
@@ -413,6 +414,42 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
413
414
  self.child1 = Prefix.objects.create(prefix="101.102.0.0/26", status=self.status, namespace=self.namespace)
414
415
  self.child2 = Prefix.objects.create(prefix="101.102.0.64/26", status=self.status, namespace=self.namespace)
415
416
 
417
+ def test_parent_exists_after_model_clean(self):
418
+ prefix = Prefix(
419
+ prefix="101.102.0.128/26",
420
+ status=self.status,
421
+ namespace=self.namespace,
422
+ type=PrefixTypeChoices.TYPE_CONTAINER,
423
+ )
424
+ prefix.clean()
425
+ self.assertEqual(prefix.parent, self.root)
426
+
427
+ def test_reparent_subnets_and_reparent_ips_call(self):
428
+ """Assert reparent_subnets and reparent_ips are only called if there is an update to either of network, namespace or prefix_length"""
429
+ prefix_ip = "101.102.0.0/28"
430
+ with self.subTest("Assert reparent_subnets"):
431
+ with patch.object(Prefix, "reparent_subnets", return_value=None) as mock_reparent_subnets:
432
+ Prefix.objects.create(prefix=prefix_ip, status=self.status, namespace=self.namespace)
433
+ mock_reparent_subnets.assert_called_once()
434
+
435
+ with patch.object(Prefix, "reparent_subnets", return_value=None) as mock_reparent_subnets:
436
+ prefix = Prefix.objects.get(prefix=prefix_ip)
437
+ prefix.description = "Sample Description"
438
+ prefix.save()
439
+ mock_reparent_subnets.assert_not_called()
440
+ prefix.delete()
441
+
442
+ with self.subTest("Assert reparent_ips"):
443
+ with patch.object(Prefix, "reparent_ips", return_value=None) as reparent_ips:
444
+ Prefix.objects.create(prefix=prefix_ip, status=self.status, namespace=self.namespace)
445
+ reparent_ips.assert_called_once()
446
+
447
+ with patch.object(Prefix, "reparent_ips", return_value=None) as reparent_ips:
448
+ prefix = Prefix.objects.get(prefix=prefix_ip)
449
+ prefix.description = "Sample Description"
450
+ prefix.save()
451
+ reparent_ips.assert_not_called()
452
+
416
453
  def test_location_queries(self):
417
454
  locations = Location.objects.all()[:4]
418
455
  for location in locations:
@@ -476,51 +513,68 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
476
513
  """Test the various ways of creating a Prefix all result in correctly populated fields."""
477
514
  with self.subTest("Creation with a prefix and status"):
478
515
  prefix = Prefix(prefix="192.0.3.0/24", status=self.status)
479
- prefix.save()
480
- self.assertEqual(prefix.network, "192.0.3.0")
481
- self.assertEqual(prefix.broadcast, "192.0.3.255")
482
- self.assertEqual(prefix.prefix_length, 24)
483
- self.assertEqual(prefix.type, PrefixTypeChoices.TYPE_NETWORK) # default value
484
- # parent field is tested exhaustively below
485
- self.assertEqual(prefix.ip_version, 4)
486
- self.assertEqual(prefix.namespace, get_default_namespace()) # default value
516
+ for method in [prefix.clean, prefix.save, prefix.refresh_from_db]:
517
+ method()
518
+ self.assertEqual(prefix.network, "192.0.3.0")
519
+ self.assertEqual(prefix.broadcast, "192.0.3.255")
520
+ self.assertEqual(prefix.prefix_length, 24)
521
+ self.assertEqual(prefix.type, PrefixTypeChoices.TYPE_NETWORK) # default value
522
+ # parent field is tested exhaustively below
523
+ self.assertEqual(prefix.ip_version, 4)
524
+ self.assertEqual(prefix.namespace, get_default_namespace()) # default value
525
+
526
+ with self.subTest("Creation with a network and prefix_length"):
527
+ prefix = Prefix(network="192.0.4.0", prefix_length=24, status=self.status)
528
+ for method in [prefix.clean, prefix.save, prefix.refresh_from_db]:
529
+ method()
530
+ self.assertEqual(prefix.network, "192.0.4.0")
531
+ self.assertEqual(prefix.broadcast, "192.0.4.255")
532
+ self.assertEqual(prefix.prefix_length, 24)
533
+ self.assertEqual(prefix.type, PrefixTypeChoices.TYPE_NETWORK) # default value
534
+ # parent field is tested exhaustively below
535
+ self.assertEqual(prefix.ip_version, 4)
536
+ self.assertEqual(prefix.namespace, get_default_namespace()) # default value
537
+ self.assertEqual(prefix.prefix, netaddr.IPNetwork("192.0.4.0/24"))
487
538
 
488
539
  with self.subTest("Creation with a network, broadcast, and prefix_length"):
489
- prefix = Prefix(network="192.0.4.0", broadcast="192.0.4.255", prefix_length=24, status=self.status)
490
- prefix.save()
491
- self.assertEqual(prefix.network, "192.0.4.0")
492
- self.assertEqual(prefix.broadcast, "192.0.4.255")
493
- self.assertEqual(prefix.prefix_length, 24)
494
- self.assertEqual(prefix.type, PrefixTypeChoices.TYPE_NETWORK) # default value
495
- # parent field is tested exhaustively below
496
- self.assertEqual(prefix.ip_version, 4)
497
- self.assertEqual(prefix.namespace, get_default_namespace()) # default value
498
- self.assertEqual(prefix.prefix, netaddr.IPNetwork("192.0.4.0/24"))
499
-
500
- with self.subTest("Creation with conflicting values - prefix takes precedence"):
540
+ prefix = Prefix(network="192.0.5.0", broadcast="192.0.5.255", prefix_length=24, status=self.status)
541
+ for method in [prefix.clean, prefix.save, prefix.refresh_from_db]:
542
+ method()
543
+ self.assertEqual(prefix.network, "192.0.5.0")
544
+ self.assertEqual(prefix.broadcast, "192.0.5.255")
545
+ self.assertEqual(prefix.prefix_length, 24)
546
+ self.assertEqual(prefix.type, PrefixTypeChoices.TYPE_NETWORK) # default value
547
+ # parent field is tested exhaustively below
548
+ self.assertEqual(prefix.ip_version, 4)
549
+ self.assertEqual(prefix.namespace, get_default_namespace()) # default value
550
+ self.assertEqual(prefix.prefix, netaddr.IPNetwork("192.0.5.0/24"))
551
+
552
+ with self.subTest("With conflicting values - prefix overrules network/broadcast/prefix_length/ip_version"):
501
553
  prefix = Prefix(
502
- prefix="192.0.5.0/24",
554
+ prefix="192.0.6.0/24",
503
555
  status=self.status,
504
556
  network="1.1.1.1",
505
557
  broadcast="2.2.2.2",
506
558
  prefix_length=27,
507
559
  ip_version=6,
508
560
  )
509
- prefix.save()
510
- self.assertEqual(prefix.network, "192.0.5.0")
511
- self.assertEqual(prefix.broadcast, "192.0.5.255")
512
- self.assertEqual(prefix.prefix_length, 24)
513
- self.assertEqual(prefix.ip_version, 4)
514
-
515
- with self.subTest("Creation with conflicting values - network and prefix_length take precedence"):
561
+ for method in [prefix.clean, prefix.save, prefix.refresh_from_db]:
562
+ method()
563
+ self.assertEqual(prefix.network, "192.0.6.0")
564
+ self.assertEqual(prefix.broadcast, "192.0.6.255")
565
+ self.assertEqual(prefix.prefix_length, 24)
566
+ self.assertEqual(prefix.ip_version, 4)
567
+
568
+ with self.subTest("With conflicting values - network/prefix_length overrule broadcast/ip_version"):
516
569
  prefix = Prefix(
517
- status=self.status, network="192.0.6.0", prefix_length=24, broadcast="192.0.6.127", ip_version=6
570
+ status=self.status, network="192.0.7.0", prefix_length=24, broadcast="192.0.7.127", ip_version=6
518
571
  )
519
- prefix.save()
520
- self.assertEqual(prefix.network, "192.0.6.0")
521
- self.assertEqual(prefix.broadcast, "192.0.6.255")
522
- self.assertEqual(prefix.prefix_length, 24)
523
- self.assertEqual(prefix.ip_version, 4)
572
+ for method in [prefix.clean, prefix.save, prefix.refresh_from_db]:
573
+ method()
574
+ self.assertEqual(prefix.network, "192.0.7.0")
575
+ self.assertEqual(prefix.broadcast, "192.0.7.255")
576
+ self.assertEqual(prefix.prefix_length, 24)
577
+ self.assertEqual(prefix.ip_version, 4)
524
578
 
525
579
  def test_tree_methods(self):
526
580
  """Test the various tree methods work as expected."""
@@ -766,6 +820,31 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
766
820
  IPAddress.objects.create(address="10.0.0.4/24", status=self.status, namespace=self.namespace)
767
821
  self.assertEqual(parent_prefix.get_first_available_ip(), "10.0.0.5/24")
768
822
 
823
+ def test_get_first_available_ip_calculate_child_ips(self):
824
+ parent_prefix = Prefix.objects.create(prefix="10.0.3.0/29", status=self.status, namespace=self.namespace)
825
+ Prefix.objects.create(prefix="10.0.3.0/30", status=self.status, namespace=self.namespace)
826
+ IPAddress(address="10.0.3.1/30", status=self.status, namespace=self.namespace).save()
827
+
828
+ self.assertEqual(parent_prefix.get_first_available_ip(), "10.0.3.2/29")
829
+
830
+ def test_get_all_ips_issue_3319(self):
831
+ # https://github.com/nautobot/nautobot/issues/3319
832
+ # Confirm that IPv4 addresses aren't caught up in the IPv6 ::/96 subnet by accident, and vice versa.
833
+ prefix_v6 = Prefix.objects.create(
834
+ prefix="::/0", type=PrefixTypeChoices.TYPE_CONTAINER, status=self.status, namespace=self.namespace
835
+ )
836
+ prefix_v4 = Prefix.objects.create(
837
+ prefix="0.0.0.0/0", type=PrefixTypeChoices.TYPE_CONTAINER, status=self.status, namespace=self.namespace
838
+ )
839
+ IPAddress.objects.create(address="::0102:0304/128", status=self.status, namespace=self.namespace)
840
+ IPAddress.objects.create(address="1.2.3.4/32", status=self.status, namespace=self.namespace)
841
+ self.assertQuerysetEqualAndNotEmpty(
842
+ prefix_v6.get_all_ips(), IPAddress.objects.filter(ip_version=6, parent__namespace=self.namespace)
843
+ )
844
+ self.assertQuerysetEqualAndNotEmpty(
845
+ prefix_v4.get_all_ips(), IPAddress.objects.filter(ip_version=4, parent__namespace=self.namespace)
846
+ )
847
+
769
848
  def test_get_utilization(self):
770
849
  # Container Prefix
771
850
  prefix = Prefix.objects.create(
@@ -921,6 +1000,12 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
921
1000
  Prefix.objects.create(prefix="ab80::/9", status=self.status, namespace=self.namespace)
922
1001
  self.assertEqual(large_prefix_v6.get_utilization(), (2**120, 2**120))
923
1002
 
1003
+ # https://github.com/nautobot/nautobot/issues/3319
1004
+ v4_10dot_address_space_in_v6 = Prefix.objects.create(
1005
+ prefix="0a00::/8", type=PrefixTypeChoices.TYPE_NETWORK, status=self.status, namespace=self.namespace
1006
+ )
1007
+ self.assertSequenceEqual(v4_10dot_address_space_in_v6.get_utilization(), (0, 2**120))
1008
+
924
1009
  #
925
1010
  # Uniqueness enforcement tests
926
1011
  #
@@ -1,13 +1,15 @@
1
1
  import re
2
2
  from unittest import skipIf
3
3
 
4
- from django.db import connection
4
+ from django.contrib.contenttypes.models import ContentType
5
+ from django.db import connection, transaction
5
6
  import netaddr
6
7
 
7
8
  from nautobot.core.testing import TestCase
8
9
  from nautobot.extras.models import Status
9
10
  from nautobot.ipam import choices
10
11
  from nautobot.ipam.models import IPAddress, Namespace, Prefix
12
+ from nautobot.users.models import ObjectPermission
11
13
 
12
14
 
13
15
  class IPAddressQuerySet(TestCase):
@@ -266,6 +268,28 @@ class IPAddressQuerySet(TestCase):
266
268
  [instance for ip, instance in self.ips.items() if "10.0.0" in ip],
267
269
  )
268
270
 
271
+ def test_lookup_not_ambiguous(self):
272
+ """Check for issues like https://github.com/nautobot/nautobot/issues/5166."""
273
+ obj_perm = ObjectPermission.objects.create(name="Test Permission", constraints={}, actions=["view"])
274
+ obj_perm.object_types.add(ContentType.objects.get_for_model(IPAddress))
275
+ obj_perm.users.add(self.user)
276
+ queryset = IPAddress.objects.select_related("parent", "nat_inside", "status")
277
+
278
+ for permission_constraint in (
279
+ {"host__family": 4},
280
+ {"host__net_host": "10.0.0.1"},
281
+ {"host__net_host_contained": "10.0.0.0/24"},
282
+ {"host__net_in": ["10.0.0.0/24"]},
283
+ ):
284
+ with self.subTest(permission_type=next(iter(permission_constraint.keys()))):
285
+ try:
286
+ with transaction.atomic():
287
+ obj_perm.constraints = permission_constraint
288
+ obj_perm.save()
289
+ list(queryset.restrict(self.user, "view"))
290
+ finally:
291
+ delattr(self.user, "_object_perm_cache")
292
+
269
293
  @skipIf(
270
294
  connection.vendor == "postgresql",
271
295
  "Not currently supported on postgresql",
@@ -454,6 +478,20 @@ class IPAddressQuerySet(TestCase):
454
478
  [instance for ip, instance in self.ips.items() if re.match(r"2001(.*)1", ip)],
455
479
  )
456
480
 
481
+ def test_get_or_create(self):
482
+ # https://github.com/nautobot/nautobot/issues/6676
483
+ ip_obj, created = IPAddress.objects.update_or_create(
484
+ defaults={
485
+ "status": self.ipaddr_status,
486
+ },
487
+ host="10.0.0.1",
488
+ mask_length="24",
489
+ namespace=self.namespace,
490
+ )
491
+ self.assertFalse(created)
492
+ self.assertEqual(str(ip_obj.address), "10.0.0.1/24")
493
+ self.assertEqual(ip_obj.parent.namespace, self.namespace)
494
+
457
495
 
458
496
  class PrefixQuerysetTestCase(TestCase):
459
497
  queryset = Prefix.objects.all()
@@ -648,6 +686,30 @@ class PrefixQuerysetTestCase(TestCase):
648
686
  prefix = Prefix.objects.filter(network__net_equals="192.168.0.0/16")[0]
649
687
  self.assertEqual(Prefix.objects.filter(prefix="192.168.0.0/16")[0], prefix)
650
688
 
689
+ def test_lookup_not_ambiguous(self):
690
+ """Check for issues like https://github.com/nautobot/nautobot/issues/5166."""
691
+ obj_perm = ObjectPermission.objects.create(name="Test Permission", constraints={}, actions=["view"])
692
+ obj_perm.object_types.add(ContentType.objects.get_for_model(Prefix))
693
+ obj_perm.users.add(self.user)
694
+ queryset = Prefix.objects.select_related("parent", "rir", "role", "status", "namespace")
695
+
696
+ for permission_constraint in (
697
+ {"network__family": 4},
698
+ {"network__net_equals": "192.168.0.0/16"},
699
+ {"network__net_contained": "192.0.0.0/8"},
700
+ {"network__net_contained_or_equal": "192.0.0.0/8"},
701
+ {"network__net_contains": "192.168.3.192/32"},
702
+ {"network__net_contains_or_equals": "192.168.3.192/32"},
703
+ ):
704
+ with self.subTest(permission_type=next(iter(permission_constraint.keys()))):
705
+ try:
706
+ with transaction.atomic():
707
+ obj_perm.constraints = permission_constraint
708
+ obj_perm.save()
709
+ list(queryset.restrict(self.user, "view"))
710
+ finally:
711
+ delattr(self.user, "_object_perm_cache")
712
+
651
713
  @skipIf(
652
714
  connection.vendor == "postgresql",
653
715
  "Not currently supported on postgresql",
@@ -1,9 +1,10 @@
1
1
  from django.test import TestCase
2
+ import netaddr
2
3
 
3
4
  from nautobot.core.forms.utils import parse_numeric_range
4
5
  from nautobot.extras.models import Status
5
- from nautobot.ipam.models import VLAN, VLANGroup
6
- from nautobot.ipam.utils import add_available_vlans
6
+ from nautobot.ipam.models import IPAddress, Namespace, Prefix, VLAN, VLANGroup
7
+ from nautobot.ipam.utils import add_available_ipaddresses, add_available_vlans
7
8
 
8
9
 
9
10
  class AddAvailableVlansTest(TestCase):
@@ -27,6 +28,44 @@ class AddAvailableVlansTest(TestCase):
27
28
  )
28
29
 
29
30
 
31
+ class AddAvailableIPsTest(TestCase):
32
+ """Tests for add_available_ipaddresses()."""
33
+
34
+ def test_add_available_ipaddresses_ipv4(self):
35
+ prefix = Prefix.objects.create(prefix="22.22.22.0/24", status=Status.objects.get_for_model(Prefix).first())
36
+ ip_status = Status.objects.get_for_model(IPAddress).first()
37
+ # .0 isn't available since this isn't a Pool prefix
38
+ available_1 = (9, "22.22.22.1/24")
39
+ ip_1 = IPAddress.objects.create(address="22.22.22.10/24", status=ip_status)
40
+ available_2 = (10, "22.22.22.11/24")
41
+ ip_2 = IPAddress.objects.create(address="22.22.22.21/24", status=ip_status)
42
+ available_3 = (233, "22.22.22.22/24")
43
+ # .255 isn't available since this isn't a Pool prefix
44
+ self.assertEqual(
45
+ add_available_ipaddresses(prefix=netaddr.IPNetwork(prefix.prefix), ipaddress_list=(ip_1, ip_2)),
46
+ [available_1, ip_1, available_2, ip_2, available_3],
47
+ )
48
+
49
+ def test_add_available_ipaddresses_ipv6(self):
50
+ namespace = Namespace.objects.create(name="add_available_ipv6")
51
+ prefix = Prefix.objects.create(
52
+ prefix="::/0", status=Status.objects.get_for_model(Prefix).first(), namespace=namespace
53
+ )
54
+ ip_status = Status.objects.get_for_model(IPAddress).first()
55
+ # .0 is available in IPv6
56
+ available_1 = (10, "::/0")
57
+ ip_1 = IPAddress.objects.create(address="::a/0", status=ip_status, namespace=namespace)
58
+ available_2 = (2**128 - 10 - 10 - 2, "::b/0")
59
+ ip_2 = IPAddress.objects.create(
60
+ address="ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff5/0", status=ip_status, namespace=namespace
61
+ )
62
+ available_3 = (10, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff6/0")
63
+ self.assertEqual(
64
+ add_available_ipaddresses(prefix=netaddr.IPNetwork(prefix.prefix), ipaddress_list=(ip_1, ip_2)),
65
+ [available_1, ip_1, available_2, ip_2, available_3],
66
+ )
67
+
68
+
30
69
  class ParseNumericRangeTest(TestCase):
31
70
  """Tests for add_available_vlans()."""
32
71
 
@@ -1054,6 +1054,9 @@ class VLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
1054
1054
  def get_deletable_object(self):
1055
1055
  return VLANGroup.objects.create(name="TEST DELETE ME")
1056
1056
 
1057
+ def get_deletable_object_pks(self):
1058
+ return [VLANGroup.objects.create(name="TEST DELETE ME").pk]
1059
+
1057
1060
 
1058
1061
  class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1059
1062
  model = VLAN