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
@@ -27,19 +27,12 @@ REARPORT_POSITIONS_MAX = 1024
27
27
  INTERFACE_MTU_MIN = 1
28
28
  INTERFACE_MTU_MAX = 32767 # Max value of a signed 16-bit integer
29
29
 
30
- VIRTUAL_IFACE_TYPES = [
31
- InterfaceTypeChoices.TYPE_VIRTUAL,
32
- InterfaceTypeChoices.TYPE_BRIDGE,
33
- InterfaceTypeChoices.TYPE_LAG,
34
- ]
35
-
36
- WIRELESS_IFACE_TYPES = [
37
- InterfaceTypeChoices.TYPE_80211A,
38
- InterfaceTypeChoices.TYPE_80211G,
39
- InterfaceTypeChoices.TYPE_80211N,
40
- InterfaceTypeChoices.TYPE_80211AC,
41
- InterfaceTypeChoices.TYPE_80211AD,
42
- ]
30
+ interface_type_by_category = {}
31
+ for category_name, category_item_tuples in InterfaceTypeChoices.CHOICES:
32
+ interface_type_by_category[category_name] = [item_tuple[0] for item_tuple in category_item_tuples]
33
+
34
+ WIRELESS_IFACE_TYPES = interface_type_by_category["Wireless"]
35
+ VIRTUAL_IFACE_TYPES = interface_type_by_category["Virtual interfaces"]
43
36
 
44
37
  NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES
45
38
 
nautobot/dcim/factory.py CHANGED
@@ -201,7 +201,12 @@ class DeviceFactory(PrimaryModelFactory):
201
201
  if extracted:
202
202
  self.software_image_files.set(extracted)
203
203
  else:
204
- self.software_image_files.set(get_random_instances(SoftwareImageFile))
204
+ self.software_image_files.set(
205
+ get_random_instances(
206
+ SoftwareImageFile.objects.filter(default_image=True)
207
+ | SoftwareImageFile.objects.filter(device_types=self.device_type)
208
+ )
209
+ )
205
210
 
206
211
  # TODO to be done after these model factories are done.
207
212
  # has_cluster = NautobotBoolIterator()
@@ -128,8 +128,8 @@ __all__ = (
128
128
  "FrontPortTemplateFilterSet",
129
129
  "InterfaceConnectionFilterSet",
130
130
  "InterfaceFilterSet",
131
- "InterfaceRedundancyGroupFilterSet",
132
131
  "InterfaceRedundancyGroupAssociationFilterSet",
132
+ "InterfaceRedundancyGroupFilterSet",
133
133
  "InterfaceTemplateFilterSet",
134
134
  "InventoryItemFilterSet",
135
135
  "LocationFilterSet",
@@ -907,6 +907,12 @@ class DeviceFilterSet(
907
907
  to_field_name="version",
908
908
  label="Software version (version or ID)",
909
909
  )
910
+ ip_addresses = MultiValueCharFilter(
911
+ method="filter_ip_addresses",
912
+ label="IP addresses (address or ID)",
913
+ distinct=True,
914
+ )
915
+ has_ip_addresses = RelatedMembershipBooleanFilter(field_name="interfaces__ip_addresses", label="Has IP addresses")
910
916
  radio_profiles = NaturalKeyOrPKMultipleChoiceFilter(
911
917
  field_name="controller_managed_device_group__radio_profiles",
912
918
  queryset=RadioProfile.objects.all(),
@@ -928,6 +934,13 @@ class DeviceFilterSet(
928
934
  label="Has wireless networks",
929
935
  )
930
936
 
937
+ def filter_ip_addresses(self, queryset, name, value):
938
+ pk_values = set(item for item in value if is_uuid(item))
939
+ addresses = set(item for item in value if item not in pk_values)
940
+
941
+ ip_queryset = IPAddress.objects.filter_address_or_pk_in(addresses, pk_values)
942
+ return queryset.filter(interfaces__ip_addresses__in=ip_queryset).distinct()
943
+
931
944
  class Meta:
932
945
  model = Device
933
946
  fields = [
@@ -1154,6 +1167,12 @@ class InterfaceFilterSet(
1154
1167
  queryset=InterfaceRedundancyGroup.objects.all(),
1155
1168
  to_field_name="name",
1156
1169
  )
1170
+ ip_addresses = MultiValueCharFilter(
1171
+ method="filter_ip_addresses",
1172
+ label="IP addresses (address or ID)",
1173
+ distinct=True,
1174
+ )
1175
+ has_ip_addresses = RelatedMembershipBooleanFilter(field_name="ip_addresses", label="Has IP addresses")
1157
1176
  virtual_device_contexts = NaturalKeyOrPKMultipleChoiceFilter(
1158
1177
  queryset=VirtualDeviceContext.objects.all(),
1159
1178
  to_field_name="name",
@@ -1164,6 +1183,13 @@ class InterfaceFilterSet(
1164
1183
  label="Has Virtual Device Context",
1165
1184
  )
1166
1185
 
1186
+ def filter_ip_addresses(self, queryset, name, value):
1187
+ pk_values = set(item for item in value if is_uuid(item))
1188
+ addresses = set(item for item in value if item not in pk_values)
1189
+
1190
+ ip_queryset = IPAddress.objects.filter_address_or_pk_in(addresses, pk_values)
1191
+ return queryset.filter(ip_addresses__in=ip_queryset).distinct()
1192
+
1167
1193
  class Meta:
1168
1194
  model = Interface
1169
1195
  fields = [
@@ -1884,7 +1910,10 @@ class ControllerFilterSet(
1884
1910
  fields = "__all__"
1885
1911
 
1886
1912
 
1887
- class ControllerManagedDeviceGroupFilterSet(NautobotFilterSet):
1913
+ class ControllerManagedDeviceGroupFilterSet(
1914
+ NautobotFilterSet,
1915
+ TenancyModelFilterSetMixin,
1916
+ ):
1888
1917
  """Filters for ControllerManagedDeviceGroup model."""
1889
1918
 
1890
1919
  q = SearchFilter(
nautobot/dcim/forms.py CHANGED
@@ -307,6 +307,17 @@ class LocationTypeFilterForm(NautobotFilterForm):
307
307
  content_types = MultipleContentTypeField(feature="locations", choices_as_strings=True, required=False)
308
308
 
309
309
 
310
+ class LocationTypeBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
311
+ pk = forms.ModelMultipleChoiceField(queryset=LocationType.objects.all(), widget=forms.MultipleHiddenInput())
312
+ description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
313
+ nestable = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
314
+ add_content_types = MultipleContentTypeField(feature="locations", required=False)
315
+ remove_content_types = MultipleContentTypeField(feature="locations", required=False)
316
+
317
+ class Meta:
318
+ nullable_fields = []
319
+
320
+
310
321
  #
311
322
  # Locations
312
323
  #
@@ -588,7 +599,7 @@ class RackBulkEditForm(
588
599
  required=False,
589
600
  widget=StaticSelect2(),
590
601
  )
591
- u_height = forms.IntegerField(required=False, label="Height (U)")
602
+ u_height = forms.IntegerField(required=False, label="Height (U)", min_value=1, max_value=100)
592
603
  desc_units = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect, label="Descending units")
593
604
  outer_width = forms.IntegerField(required=False, min_value=1)
594
605
  outer_depth = forms.IntegerField(required=False, min_value=1)
@@ -784,6 +795,7 @@ class DeviceFamilyForm(NautobotModelForm):
784
795
  fields = [
785
796
  "name",
786
797
  "description",
798
+ "tags",
787
799
  ]
788
800
 
789
801
 
@@ -796,7 +808,7 @@ class DeviceFamilyFilterForm(NautobotFilterForm):
796
808
  tags = TagFilterField(model)
797
809
 
798
810
 
799
- class DeviceFamilyBulkEditForm(NautobotBulkEditForm, TagsBulkEditFormMixin):
811
+ class DeviceFamilyBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
800
812
  pk = forms.ModelMultipleChoiceField(queryset=DeviceFamily.objects.all(), widget=forms.MultipleHiddenInput())
801
813
  description = forms.CharField(required=False)
802
814
 
@@ -878,7 +890,7 @@ class DeviceTypeBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
878
890
  manufacturer = DynamicModelChoiceField(queryset=Manufacturer.objects.all(), required=False)
879
891
  device_family = DynamicModelChoiceField(queryset=DeviceFamily.objects.all(), required=False)
880
892
  software_image_files = DynamicModelMultipleChoiceField(queryset=SoftwareImageFile.objects.all(), required=False)
881
- u_height = forms.IntegerField(required=False)
893
+ u_height = forms.IntegerField(required=False, min_value=0)
882
894
  is_full_depth = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect(), label="Is full depth")
883
895
  comments = CommentField(label="Comments", required=False)
884
896
 
@@ -1655,6 +1667,8 @@ class ComponentTemplateImportForm(BootstrapMixin, CustomFieldModelCSVForm):
1655
1667
  netbox-community/devicetype-library repository.
1656
1668
  """
1657
1669
 
1670
+ Meta: type # to be defined by concrete subclasses
1671
+
1658
1672
  def __init__(self, data=None, *args, **kwargs):
1659
1673
  super().__init__(data, *args, **kwargs)
1660
1674
 
@@ -2127,6 +2141,8 @@ class DeviceBulkEditForm(
2127
2141
  rack_group = DynamicModelChoiceField(
2128
2142
  queryset=RackGroup.objects.all(), required=False, query_params={"location": "$location"}
2129
2143
  )
2144
+ cluster = DynamicModelChoiceField(queryset=Cluster.objects.all(), required=False)
2145
+ comments = CommentField(widget=SmallTextarea, label="Comments")
2130
2146
  tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
2131
2147
  platform = DynamicModelChoiceField(queryset=Platform.objects.all(), required=False)
2132
2148
  serial = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False, label="Serial Number")
@@ -2150,6 +2166,8 @@ class DeviceBulkEditForm(
2150
2166
  "position",
2151
2167
  "face",
2152
2168
  "rack_group",
2169
+ "cluster",
2170
+ "comments",
2153
2171
  "secrets_group",
2154
2172
  "device_redundancy_group",
2155
2173
  "device_redundancy_group_priority",
@@ -3214,9 +3232,6 @@ class InterfaceBulkEditForm(
3214
3232
  untagged_vlan = DynamicModelChoiceField(
3215
3233
  queryset=VLAN.objects.all(),
3216
3234
  required=False,
3217
- query_params={
3218
- "locations": "null",
3219
- },
3220
3235
  )
3221
3236
  tagged_vlans = DynamicModelMultipleChoiceField(
3222
3237
  queryset=VLAN.objects.all(),
@@ -3266,8 +3281,12 @@ class InterfaceBulkEditForm(
3266
3281
  # Limit VLAN choices by Location
3267
3282
  if locations.count() == 1:
3268
3283
  location = locations.first()
3269
- self.fields["untagged_vlan"].widget.add_query_param("locations", location.pk)
3284
+ # In the case of a single location, use the available_on_device query param to limit untagged VLAN choices
3285
+ # to those available on the devices in that location and in the ancestors of the location.
3286
+ self.fields["untagged_vlan"].widget.add_query_param("available_on_device", device.pk)
3270
3287
  self.fields["tagged_vlans"].widget.add_query_param("locations", location.pk)
3288
+ else:
3289
+ self.fields["tagged_vlans"].widget.add_query_param("locations", "null")
3271
3290
 
3272
3291
  # Restrict parent/bridge/LAG interface assignment by device (or VC master)
3273
3292
  if device_count == 1:
@@ -4982,10 +5001,6 @@ class ControllerForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm):
4982
5001
  queryset=Platform.objects.all(),
4983
5002
  required=False,
4984
5003
  )
4985
- tenant = DynamicModelChoiceField(
4986
- queryset=Tenant.objects.all(),
4987
- required=False,
4988
- )
4989
5004
  external_integration = DynamicModelChoiceField(
4990
5005
  queryset=ExternalIntegration.objects.all(),
4991
5006
  required=False,
@@ -5007,6 +5022,7 @@ class ControllerForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm):
5007
5022
  "role",
5008
5023
  "description",
5009
5024
  "platform",
5025
+ "tenant_group",
5010
5026
  "tenant",
5011
5027
  "location",
5012
5028
  "capabilities",
@@ -5059,6 +5075,7 @@ class ControllerFilterForm(
5059
5075
  "description",
5060
5076
  "location",
5061
5077
  "platform",
5078
+ "tenant_group",
5062
5079
  "tenant",
5063
5080
  "external_integration",
5064
5081
  "controller_device",
@@ -5085,7 +5102,7 @@ class ControllerBulkEditForm(
5085
5102
  required=False,
5086
5103
  )
5087
5104
  capabilities = JSONArrayFormField(
5088
- choices=add_blank_choice(ControllerCapabilitiesChoices),
5105
+ choices=ControllerCapabilitiesChoices,
5089
5106
  base_field=forms.CharField(),
5090
5107
  required=False,
5091
5108
  )
@@ -5114,15 +5131,18 @@ class ControllerBulkEditForm(
5114
5131
  "description",
5115
5132
  "location",
5116
5133
  "platform",
5117
- "tenant",
5118
5134
  "external_integration",
5119
5135
  "controller_device",
5120
5136
  "controller_device_redundancy_group",
5121
5137
  "tags",
5122
5138
  )
5139
+ nullable_fields = (
5140
+ "tenant",
5141
+ "capabilities",
5142
+ )
5123
5143
 
5124
5144
 
5125
- class ControllerManagedDeviceGroupForm(NautobotModelForm):
5145
+ class ControllerManagedDeviceGroupForm(NautobotModelForm, TenancyForm):
5126
5146
  """ControllerManagedDeviceGroup create/edit form."""
5127
5147
 
5128
5148
  controller = DynamicModelChoiceField(queryset=Controller.objects.all(), required=True)
@@ -5139,12 +5159,15 @@ class ControllerManagedDeviceGroupForm(NautobotModelForm):
5139
5159
  fields = (
5140
5160
  "controller",
5141
5161
  "name",
5162
+ "description",
5142
5163
  "devices",
5143
5164
  "parent",
5144
5165
  "capabilities",
5145
5166
  "weight",
5146
5167
  "radio_profiles",
5147
5168
  "tags",
5169
+ "tenant_group",
5170
+ "tenant",
5148
5171
  )
5149
5172
 
5150
5173
  def __init__(self, *args, **kwargs):
@@ -5159,12 +5182,13 @@ class ControllerManagedDeviceGroupForm(NautobotModelForm):
5159
5182
  return instance
5160
5183
 
5161
5184
 
5162
- class ControllerManagedDeviceGroupFilterForm(NautobotFilterForm):
5185
+ class ControllerManagedDeviceGroupFilterForm(NautobotFilterForm, TenancyFilterForm):
5163
5186
  """ControllerManagedDeviceGroup basic filter form."""
5164
5187
 
5165
5188
  model = ControllerManagedDeviceGroup
5166
5189
  q = forms.CharField(required=False, label="Search")
5167
5190
  name = forms.CharField(required=False, label="Name")
5191
+ description = forms.CharField(required=False, label="Description")
5168
5192
  controller = DynamicModelChoiceField(
5169
5193
  queryset=Controller.objects.all(),
5170
5194
  required=False,
@@ -5185,11 +5209,14 @@ class ControllerManagedDeviceGroupFilterForm(NautobotFilterForm):
5185
5209
  field_order = (
5186
5210
  "q",
5187
5211
  "name",
5212
+ "description",
5188
5213
  "controller",
5189
5214
  "parent",
5190
5215
  "weight",
5191
5216
  "subtree",
5192
5217
  "tags",
5218
+ "tenant",
5219
+ "tenant_group",
5193
5220
  )
5194
5221
 
5195
5222
 
@@ -5214,10 +5241,11 @@ class ControllerManagedDeviceGroupBulkEditForm(TagsBulkEditFormMixin, NautobotBu
5214
5241
  label="Remove Radio Profiles",
5215
5242
  )
5216
5243
  capabilities = JSONArrayFormField(
5217
- choices=add_blank_choice(ControllerCapabilitiesChoices),
5244
+ choices=ControllerCapabilitiesChoices,
5218
5245
  base_field=forms.CharField(),
5219
5246
  required=False,
5220
5247
  )
5248
+ tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
5221
5249
 
5222
5250
  class Meta:
5223
5251
  model = ControllerManagedDeviceGroup
@@ -5225,9 +5253,12 @@ class ControllerManagedDeviceGroupBulkEditForm(TagsBulkEditFormMixin, NautobotBu
5225
5253
  "controller",
5226
5254
  "parent",
5227
5255
  "weight",
5228
- "capabilities",
5229
5256
  "tags",
5230
5257
  )
5258
+ nullable_fields = (
5259
+ "tenant",
5260
+ "capabilities",
5261
+ )
5231
5262
 
5232
5263
 
5233
5264
  #
@@ -98,13 +98,13 @@ class CableType(OptimizedNautobotObjectType):
98
98
 
99
99
  def resolve_termination_a_type(self, args):
100
100
  if self.termination_a_type:
101
- model = self.termination_a_type.model_class()
101
+ model = self.termination_a_type.model_class() # pylint: disable=no-member
102
102
  return f"{model._meta.app_label}.{model._meta.model_name}"
103
103
  return None
104
104
 
105
105
  def resolve_termination_b_type(self, args):
106
106
  if self.termination_b_type:
107
- model = self.termination_b_type.model_class()
107
+ model = self.termination_b_type.model_class() # pylint: disable=no-member
108
108
  return f"{model._meta.app_label}.{model._meta.model_name}"
109
109
  return None
110
110
 
@@ -0,0 +1,25 @@
1
+ # Generated by Django 4.2.16 on 2024-11-21 12:35
2
+
3
+ from django.db import migrations, models
4
+ import django.db.models.deletion
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+ dependencies = [
9
+ ("tenancy", "0009_update_all_charfields_max_length_to_255"),
10
+ ("dcim", "0066_controllermanageddevicegroup_radio_profiles_and_more"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name="controllermanageddevicegroup",
16
+ name="tenant",
17
+ field=models.ForeignKey(
18
+ blank=True,
19
+ null=True,
20
+ on_delete=django.db.models.deletion.PROTECT,
21
+ related_name="controller_managed_device_groups",
22
+ to="tenancy.tenant",
23
+ ),
24
+ ),
25
+ ]
@@ -63,12 +63,12 @@ __all__ = (
63
63
  "Device",
64
64
  "DeviceBay",
65
65
  "DeviceBayTemplate",
66
+ "DeviceFamily",
66
67
  "DeviceRedundancyGroup",
67
68
  "DeviceType",
68
69
  "DeviceTypeToSoftwareImageFile",
69
70
  "FrontPort",
70
71
  "FrontPortTemplate",
71
- "DeviceFamily",
72
72
  "Interface",
73
73
  "InterfaceRedundancyGroup",
74
74
  "InterfaceRedundancyGroupAssociation",
@@ -100,7 +100,7 @@ class ComponentTemplateModel(
100
100
  def get_absolute_url(self, api=False):
101
101
  # TODO: in the new UI, this should be able to link directly to the object, instead of the device-type.
102
102
  if not api:
103
- return self.device_type.get_absolute_url(api=api)
103
+ return self.device_type.get_absolute_url(api=api) # pylint: disable=no-member
104
104
  return super().get_absolute_url(api=api)
105
105
 
106
106
  def instantiate_model(self, model, device, **kwargs):
@@ -460,7 +460,7 @@ class DeviceBayTemplate(ComponentTemplateModel):
460
460
  return self.instantiate_model(model=DeviceBay, device=device)
461
461
 
462
462
  def clean(self):
463
- if self.device_type and self.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT:
463
+ if self.device_type and self.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT: # pylint: disable=no-member
464
464
  raise ValidationError(
465
465
  f'Subdevice role of device type ({self.device_type}) must be set to "parent" to allow device bays.'
466
466
  )
@@ -138,7 +138,7 @@ class ModularComponentModel(ComponentModel):
138
138
  @property
139
139
  def parent(self):
140
140
  """Device that this component belongs to, walking up module inheritance if necessary."""
141
- return self.module.device if self.module else self.device
141
+ return self.module.device if self.module else self.device # pylint: disable=no-member
142
142
 
143
143
  def render_name_template(self, save=False):
144
144
  """
@@ -161,9 +161,9 @@ class ModularComponentModel(ComponentModel):
161
161
 
162
162
  The deeply nested interface would be named "A{module.parent}" after calling this method.
163
163
  """
164
- if self.module and self.module.parent_module_bay and "{module" in self.name:
164
+ if self.module and self.module.parent_module_bay and "{module" in self.name: # pylint: disable=no-member
165
165
  name = ""
166
- module_bay = self.module.parent_module_bay
166
+ module_bay = self.module.parent_module_bay # pylint: disable=no-member
167
167
  positions = []
168
168
  while module_bay is not None:
169
169
  position = getattr(module_bay, "position", None)
@@ -291,11 +291,11 @@ class PathEndpoint(models.Model):
291
291
  return []
292
292
 
293
293
  # Construct the complete path
294
- path = [self, *self._path.get_path()]
294
+ path = [self, *self._path.get_path()] # pylint: disable=no-member
295
295
  while (len(path) + 1) % 3:
296
296
  # Pad to ensure we have complete three-tuples (e.g. for paths that end at a RearPort)
297
297
  path.append(None)
298
- path.append(self._path.destination)
298
+ path.append(self._path.destination) # pylint: disable=no-member
299
299
 
300
300
  # Return the path as a list of three-tuples (A termination, cable, B termination)
301
301
  return list(zip(*[iter(path)] * 3))
@@ -555,7 +555,7 @@ class BaseInterface(RelationshipModel):
555
555
 
556
556
  def clean(self):
557
557
  # Remove untagged VLAN assignment for non-802.1Q interfaces
558
- if not self.mode and self.untagged_vlan is not None:
558
+ if not self.mode and self.untagged_vlan is not None: # pylint: disable=no-member # Intf/VMIntf both have untagged_vlan
559
559
  raise ValidationError({"untagged_vlan": "Mode must be set when specifying untagged_vlan"})
560
560
 
561
561
  def save(self, *args, **kwargs):
@@ -569,8 +569,8 @@ class BaseInterface(RelationshipModel):
569
569
  self.status = status
570
570
 
571
571
  # Only "tagged" interfaces may have tagged VLANs assigned. ("tagged all" implies all VLANs are assigned.)
572
- if self.present_in_database and self.mode != InterfaceModeChoices.MODE_TAGGED:
573
- self.tagged_vlans.clear()
572
+ if self.present_in_database and self.mode != InterfaceModeChoices.MODE_TAGGED: # pylint: disable=no-member
573
+ self.tagged_vlans.clear() # pylint: disable=no-member # Intf/VMIntf both have tagged_vlans
574
574
 
575
575
  return super().save(*args, **kwargs)
576
576
 
@@ -710,18 +710,19 @@ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInter
710
710
  )
711
711
 
712
712
  # An interface's parent must belong to the same device or virtual chassis
713
- if self.parent and self.parent_interface.parent != self.parent:
713
+ if self.parent and self.parent_interface.parent != self.parent: # pylint: disable=no-member
714
714
  if getattr(self.parent, "virtual_chassis", None) is None:
715
715
  raise ValidationError(
716
- {
717
- "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs to a different device "
718
- f"({self.parent_interface.parent})."
716
+ { # pylint: disable=no-member # false positive on parent_interface.parent
717
+ "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs "
718
+ f"to a different device ({self.parent_interface.parent})."
719
719
  }
720
720
  )
721
- elif self.parent_interface.parent.virtual_chassis != self.parent.virtual_chassis:
721
+ elif self.parent_interface.parent.virtual_chassis != self.parent.virtual_chassis: # pylint: disable=no-member
722
722
  raise ValidationError(
723
- {
724
- "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs to {self.parent_interface.parent}, which "
723
+ { # pylint: disable=no-member # false positive on parent_interface.parent
724
+ "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs "
725
+ f"to {self.parent_interface.parent}, which "
725
726
  f"is not part of virtual chassis {self.parent.virtual_chassis}."
726
727
  }
727
728
  )
@@ -754,21 +755,22 @@ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInter
754
755
  raise ValidationError({"bridge": "An interface cannot be bridged to itself."})
755
756
 
756
757
  # A bridged interface belong to the same device or virtual chassis
757
- if self.parent and self.bridge.parent != self.parent:
758
+ if self.parent and self.bridge.parent != self.parent: # pylint: disable=no-member
758
759
  if getattr(self.parent, "virtual_chassis", None) is None:
759
760
  raise ValidationError(
760
761
  {
761
762
  "bridge": (
763
+ # pylint: disable=no-member # false positive on bridge.parent
762
764
  f"The selected bridge interface ({self.bridge}) belongs to a different device "
763
765
  f"({self.bridge.parent})."
764
766
  )
765
767
  }
766
768
  )
767
- elif self.bridge.parent.virtual_chassis_id != self.parent.virtual_chassis_id:
769
+ elif self.bridge.parent.virtual_chassis_id != self.parent.virtual_chassis_id: # pylint: disable=no-member
768
770
  raise ValidationError(
769
771
  {
770
772
  "bridge": (
771
- f"The selected bridge interface ({self.bridge}) belongs to {self.bridge.parent}, which "
773
+ f"The selected bridge interface ({self.bridge}) belongs to {self.bridge.parent}, which " # pylint: disable=no-member
772
774
  f"is not part of virtual chassis {self.parent.virtual_chassis}."
773
775
  )
774
776
  }
@@ -1084,8 +1086,8 @@ class DeviceBay(ComponentModel):
1084
1086
  super().clean()
1085
1087
 
1086
1088
  # Validate that the parent Device can have DeviceBays
1087
- if not self.device.device_type.is_parent_device:
1088
- raise ValidationError(f"This type of device ({self.device.device_type}) does not support device bays.")
1089
+ if not self.device.device_type.is_parent_device: # pylint: disable=no-member
1090
+ raise ValidationError(f"This type of device ({self.device.device_type}) does not support device bays.") # pylint: disable=no-member
1089
1091
 
1090
1092
  # Cannot install a device into itself, obviously
1091
1093
  if self.device == self.installed_device:
@@ -48,6 +48,7 @@ __all__ = (
48
48
  "Controller",
49
49
  "ControllerManagedDeviceGroup",
50
50
  "Device",
51
+ "DeviceFamily",
51
52
  "DeviceRedundancyGroup",
52
53
  "DeviceType",
53
54
  "InterfaceVDCAssignment",
@@ -823,6 +824,7 @@ class Device(PrimaryModel, ConfigContextModel):
823
824
 
824
825
  # If any software image file is specified, validate that
825
826
  # each of the software image files belongs to the device's device type or is a default image
827
+ # TODO: this is incorrect as we cannot validate a ManyToMany during clean() - nautobot/nautobot#6344
826
828
  for image_file in self.software_image_files.all():
827
829
  if not image_file.default_image and self.device_type not in image_file.device_types.all():
828
830
  raise ValidationError(
@@ -1481,6 +1483,13 @@ class ControllerManagedDeviceGroup(TreeModel, PrimaryModel):
1481
1483
  null=True,
1482
1484
  help_text="List of capabilities supported by the controller device group, these capabilities are used to enhance views in Nautobot.",
1483
1485
  )
1486
+ tenant = models.ForeignKey(
1487
+ to="tenancy.Tenant",
1488
+ on_delete=models.PROTECT,
1489
+ related_name="controller_managed_device_groups",
1490
+ blank=True,
1491
+ null=True,
1492
+ )
1484
1493
 
1485
1494
  class Meta:
1486
1495
  ordering = ("weight",)
@@ -1500,7 +1509,7 @@ class ControllerManagedDeviceGroup(TreeModel, PrimaryModel):
1500
1509
  if self.controller == self._original_controller and self.parent == self._original_parent:
1501
1510
  return
1502
1511
 
1503
- if self.parent and self.controller and self.controller != self.parent.controller:
1512
+ if self.parent and self.controller and self.controller != self.parent.controller: # pylint: disable=no-member
1504
1513
  raise ValidationError(
1505
1514
  {"controller": "Controller device group must have the same controller as the parent group."}
1506
1515
  )
@@ -283,7 +283,7 @@ class Location(TreeModel, PrimaryModel):
283
283
  # We shouldn't have a parent, *unless* our own location type is permitted to be nested.
284
284
  if self.parent is not None:
285
285
  if self.location_type.nestable:
286
- if self.parent.location_type != self.location_type:
286
+ if self.parent.location_type != self.location_type: # pylint: disable=no-member
287
287
  raise ValidationError(
288
288
  {
289
289
  "parent": f"A Location of type {self.location_type} may only have "
@@ -304,7 +304,7 @@ class Location(TreeModel, PrimaryModel):
304
304
 
305
305
  # Is the parent location of a correct type?
306
306
  if self.location_type.nestable:
307
- if self.parent.location_type not in (self.location_type, self.location_type.parent):
307
+ if self.parent.location_type not in (self.location_type, self.location_type.parent): # pylint: disable=no-member
308
308
  raise ValidationError(
309
309
  {
310
310
  "parent": f"A Location of type {self.location_type} can only have a Location "
@@ -312,7 +312,7 @@ class Location(TreeModel, PrimaryModel):
312
312
  }
313
313
  )
314
314
  else:
315
- if self.parent.location_type != self.location_type.parent:
315
+ if self.parent.location_type != self.location_type.parent: # pylint: disable=no-member
316
316
  raise ValidationError(
317
317
  {
318
318
  "parent": f"A Location of type {self.location_type} can only have a Location "
@@ -73,11 +73,11 @@ class PowerPanel(PrimaryModel):
73
73
  if self.rack_group:
74
74
  if (
75
75
  self.location is not None
76
- and self.rack_group.location is not None
77
- and self.rack_group.location not in self.location.ancestors(include_self=True)
76
+ and self.rack_group.location is not None # pylint: disable=no-member
77
+ and self.rack_group.location not in self.location.ancestors(include_self=True) # pylint: disable=no-member
78
78
  ):
79
79
  raise ValidationError(
80
- {
80
+ { # pylint: disable=no-member # false positive on rack_group.location
81
81
  "rack_group": f'Rack group "{self.rack_group}" belongs to a location '
82
82
  f'("{self.rack_group.location}") that does not contain "{self.location}".'
83
83
  }
@@ -151,9 +151,10 @@ class PowerFeed(PrimaryModel, PathEndpoint, CableTermination):
151
151
  super().clean()
152
152
 
153
153
  # Rack must belong to same Location as PowerPanel
154
- if self.rack and self.rack.location != self.power_panel.location:
154
+ if self.rack and self.rack.location != self.power_panel.location: # pylint: disable=no-member
155
155
  raise ValidationError(
156
- f"Rack {self.rack} ({self.rack.location}) and power panel {self.power_panel} ({self.power_panel.location}) are in different locations"
156
+ f"Rack {self.rack} ({self.rack.location}) and " # pylint: disable=no-member
157
+ f"power panel {self.power_panel} ({self.power_panel.location}) are in different locations"
157
158
  )
158
159
 
159
160
  # AC voltage cannot be negative
@@ -77,11 +77,11 @@ class RackGroup(TreeModel, OrganizationalModel):
77
77
  # Parent RackGroup (if any) must belong to the same or ancestor Location
78
78
  if (
79
79
  self.parent is not None
80
- and self.parent.location is not None
81
- and self.parent.location not in self.location.ancestors(include_self=True)
80
+ and self.parent.location is not None # pylint: disable=no-member
81
+ and self.parent.location not in self.location.ancestors(include_self=True) # pylint: disable=no-member
82
82
  ):
83
83
  raise ValidationError(
84
- {
84
+ { # pylint: disable=no-member # false positive on parent.location
85
85
  "location": f'Location "{self.location}" is not descended from '
86
86
  f'parent rack group "{self.parent}" location "{self.parent.location}".'
87
87
  }
@@ -296,7 +296,7 @@ class Rack(PrimaryModel):
296
296
  # Determine which devices the user has permission to view
297
297
  permitted_device_ids = []
298
298
  if user is not None:
299
- permitted_device_ids = self.devices.restrict(user, "view").values_list("pk", flat=True)
299
+ permitted_device_ids = self.devices.restrict(user, "view").values_list("pk", flat=True) # pylint: disable=no-member
300
300
 
301
301
  for device in queryset:
302
302
  if expand_devices: