nautobot 2.0.0a2__py3-none-any.whl → 2.0.0b1__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.
Files changed (1029) hide show
  1. nautobot/__init__.py +1 -5
  2. nautobot/apps/api.py +6 -8
  3. nautobot/apps/forms.py +0 -2
  4. nautobot/apps/ui.py +0 -8
  5. nautobot/circuits/api/serializers.py +9 -119
  6. nautobot/circuits/api/urls.py +1 -1
  7. nautobot/circuits/api/views.py +0 -1
  8. nautobot/circuits/choices.py +0 -2
  9. nautobot/circuits/filters.py +7 -6
  10. nautobot/circuits/forms.py +3 -73
  11. nautobot/circuits/migrations/0001_initial_part_1.py +0 -1
  12. nautobot/circuits/migrations/0002_initial_part_2.py +0 -1
  13. nautobot/circuits/migrations/0003_auto_slug.py +0 -1
  14. nautobot/circuits/migrations/0004_increase_provider_account_length.py +0 -1
  15. nautobot/circuits/migrations/0005_providernetwork.py +0 -1
  16. nautobot/circuits/migrations/0006_cache_circuit_terminations.py +0 -1
  17. nautobot/circuits/migrations/0007_circuitterminations_primary_model.py +0 -1
  18. nautobot/circuits/migrations/0008_add_natural_indexing.py +0 -1
  19. nautobot/circuits/migrations/0009_circuittermination_location.py +0 -1
  20. nautobot/circuits/migrations/0010_rename_foreign_keys_and_related_names.py +0 -1
  21. nautobot/circuits/migrations/0011_remove_site_foreign_key_from_circuit_termination_class.py +0 -1
  22. nautobot/circuits/migrations/0012_created_datetime.py +0 -1
  23. nautobot/circuits/migrations/0013_alter_circuittermination__path.py +0 -1
  24. nautobot/circuits/migrations/0014_related_name_changes.py +1 -2
  25. nautobot/circuits/migrations/0015_remove_circuittype_provider_slug.py +20 -0
  26. nautobot/circuits/migrations/0016_tagsfield.py +34 -0
  27. nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
  28. nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
  29. nautobot/circuits/models.py +3 -93
  30. nautobot/circuits/navigation.py +14 -69
  31. nautobot/circuits/signals.py +0 -2
  32. nautobot/circuits/tables.py +42 -5
  33. nautobot/circuits/templates/circuits/circuit_retrieve.html +1 -1
  34. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -1
  35. nautobot/circuits/templates/circuits/circuittype_retrieve.html +1 -1
  36. nautobot/circuits/templates/circuits/provider_create.html +0 -1
  37. nautobot/circuits/templates/circuits/provider_retrieve.html +1 -1
  38. nautobot/circuits/tests/integration/test_relationships.py +13 -16
  39. nautobot/circuits/tests/test_api.py +13 -43
  40. nautobot/circuits/tests/test_filters.py +20 -15
  41. nautobot/circuits/tests/test_models.py +7 -3
  42. nautobot/circuits/tests/test_views.py +57 -67
  43. nautobot/circuits/views.py +18 -9
  44. nautobot/core/api/__init__.py +8 -2
  45. nautobot/core/api/authentication.py +0 -3
  46. nautobot/core/api/fields.py +15 -6
  47. nautobot/core/api/filter_backends.py +3 -2
  48. nautobot/core/api/metadata.py +237 -30
  49. nautobot/core/api/mixins.py +94 -0
  50. nautobot/core/api/pagination.py +3 -3
  51. nautobot/core/api/parsers.py +154 -0
  52. nautobot/core/api/renderers.py +153 -2
  53. nautobot/core/api/schema.py +47 -3
  54. nautobot/core/api/serializers.py +377 -37
  55. nautobot/core/api/urls.py +11 -3
  56. nautobot/core/api/utils.py +174 -2
  57. nautobot/core/api/versioning.py +32 -10
  58. nautobot/core/api/views.py +266 -75
  59. nautobot/core/apps/__init__.py +138 -221
  60. nautobot/core/celery/__init__.py +112 -41
  61. nautobot/core/celery/backends.py +19 -13
  62. nautobot/core/celery/control.py +46 -0
  63. nautobot/core/celery/encoders.py +53 -0
  64. nautobot/core/celery/log.py +38 -0
  65. nautobot/core/celery/schedulers.py +23 -4
  66. nautobot/core/celery/task.py +1 -16
  67. nautobot/core/checks.py +0 -27
  68. nautobot/core/choices.py +21 -113
  69. nautobot/core/{cli.py → cli/__init__.py} +1 -2
  70. nautobot/core/cli/__main__.py +3 -0
  71. nautobot/core/constants.py +25 -43
  72. nautobot/core/context_processors.py +12 -0
  73. nautobot/core/filters.py +2 -2
  74. nautobot/core/forms/__init__.py +0 -4
  75. nautobot/core/forms/fields.py +39 -68
  76. nautobot/core/forms/forms.py +27 -27
  77. nautobot/core/forms/utils.py +7 -59
  78. nautobot/core/forms/widgets.py +0 -1
  79. nautobot/core/graphql/__init__.py +2 -2
  80. nautobot/core/graphql/schema.py +4 -27
  81. nautobot/core/jobs/__init__.py +75 -0
  82. nautobot/core/management/commands/build_ui.py +255 -0
  83. nautobot/core/management/commands/celery.py +0 -1
  84. nautobot/core/management/commands/generate_test_data.py +18 -13
  85. nautobot/core/management/commands/post_upgrade.py +24 -24
  86. nautobot/core/management/commands/validate_models.py +0 -1
  87. nautobot/core/middleware.py +0 -1
  88. nautobot/core/models/__init__.py +26 -1
  89. nautobot/core/models/fields.py +24 -5
  90. nautobot/core/models/generics.py +2 -46
  91. nautobot/core/models/managers.py +5 -0
  92. nautobot/core/models/name_color_content_types.py +1 -19
  93. nautobot/core/models/tree_queries.py +14 -4
  94. nautobot/core/models/utils.py +9 -10
  95. nautobot/core/models/validators.py +17 -8
  96. nautobot/core/releases.py +8 -10
  97. nautobot/core/settings.py +81 -53
  98. nautobot/core/tables.py +5 -5
  99. nautobot/core/tasks.py +4 -7
  100. nautobot/core/templates/base.html +1 -49
  101. nautobot/core/templates/base_django.html +49 -0
  102. nautobot/core/templates/base_react.html +55 -0
  103. nautobot/core/templates/buttons/export.html +6 -4
  104. nautobot/core/templates/generic/object_bulk_create.html +10 -21
  105. nautobot/core/templates/generic/object_list.html +4 -1
  106. nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
  107. nautobot/core/templates/inc/footer.html +1 -0
  108. nautobot/core/templates/inc/javascript.html +0 -14
  109. nautobot/core/templates/inc/nav_menu.html +28 -33
  110. nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
  111. nautobot/core/templates/inc/relationships_table_rows.html +2 -2
  112. nautobot/core/templates/nautobot_config.py.j2 +8 -25
  113. nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
  114. nautobot/core/templates/rest_framework/api.html +8 -0
  115. nautobot/core/templatetags/buttons.py +32 -29
  116. nautobot/core/templatetags/helpers.py +1 -1
  117. nautobot/core/testing/__init__.py +47 -44
  118. nautobot/core/testing/api.py +365 -47
  119. nautobot/core/testing/filters.py +12 -7
  120. nautobot/core/testing/integration.py +1 -1
  121. nautobot/core/testing/migrations.py +2 -0
  122. nautobot/core/testing/mixins.py +22 -12
  123. nautobot/core/testing/schema.py +2 -1
  124. nautobot/core/testing/views.py +28 -51
  125. nautobot/core/tests/integration/test_filters.py +17 -8
  126. nautobot/core/tests/integration/test_navbar.py +11 -34
  127. nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
  128. nautobot/core/tests/nautobot_config.py +2 -3
  129. nautobot/core/tests/runner.py +0 -1
  130. nautobot/core/tests/test_api.py +290 -24
  131. nautobot/core/tests/test_authentication.py +57 -14
  132. nautobot/core/tests/test_checks.py +0 -7
  133. nautobot/core/tests/test_choices.py +0 -1
  134. nautobot/core/tests/test_filters.py +117 -110
  135. nautobot/core/tests/test_forms.py +47 -110
  136. nautobot/core/tests/test_graphql.py +158 -135
  137. nautobot/core/tests/test_logging.py +4 -1
  138. nautobot/core/tests/test_managers.py +3 -5
  139. nautobot/core/tests/test_models.py +2 -0
  140. nautobot/core/tests/test_ordering.py +0 -2
  141. nautobot/core/tests/test_paginator.py +3 -1
  142. nautobot/core/tests/test_releases.py +12 -12
  143. nautobot/core/tests/test_templatetags_helpers.py +7 -4
  144. nautobot/core/tests/test_utils.py +112 -78
  145. nautobot/core/tests/test_views.py +12 -17
  146. nautobot/core/tests/test_views_utils.py +6 -9
  147. nautobot/core/utils/data.py +17 -0
  148. nautobot/core/utils/deprecation.py +13 -20
  149. nautobot/core/utils/filtering.py +53 -9
  150. nautobot/core/utils/git.py +12 -4
  151. nautobot/core/utils/lookup.py +3 -1
  152. nautobot/core/utils/requests.py +23 -116
  153. nautobot/core/views/__init__.py +1 -2
  154. nautobot/core/views/generic.py +131 -119
  155. nautobot/core/views/mixins.py +53 -62
  156. nautobot/core/views/paginator.py +0 -1
  157. nautobot/core/views/renderers.py +14 -12
  158. nautobot/core/views/utils.py +87 -4
  159. nautobot/dcim/api/serializers.py +160 -672
  160. nautobot/dcim/api/urls.py +1 -1
  161. nautobot/dcim/api/views.py +7 -46
  162. nautobot/dcim/choices.py +2 -25
  163. nautobot/dcim/elevations.py +0 -1
  164. nautobot/dcim/factory.py +15 -4
  165. nautobot/dcim/filters/__init__.py +42 -13
  166. nautobot/dcim/form_mixins.py +1 -27
  167. nautobot/dcim/forms.py +58 -797
  168. nautobot/dcim/management/commands/trace_paths.py +0 -1
  169. nautobot/dcim/migrations/0001_initial_part_1.py +0 -1
  170. nautobot/dcim/migrations/0002_initial_part_2.py +0 -1
  171. nautobot/dcim/migrations/0003_initial_part_3.py +0 -1
  172. nautobot/dcim/migrations/0004_initial_part_4.py +0 -1
  173. nautobot/dcim/migrations/0005_device_local_context_schema.py +0 -1
  174. nautobot/dcim/migrations/0006_auto_slug.py +0 -1
  175. nautobot/dcim/migrations/0007_device_secrets_group.py +0 -1
  176. nautobot/dcim/migrations/0008_increase_all_serial_lengths.py +0 -1
  177. nautobot/dcim/migrations/0009_add_natural_indexing.py +0 -1
  178. nautobot/dcim/migrations/0010_interface_status.py +0 -1
  179. nautobot/dcim/migrations/0011_interface_status_data_migration.py +0 -1
  180. nautobot/dcim/migrations/0012_interface_parent_bridge.py +0 -1
  181. nautobot/dcim/migrations/0013_location_location_type.py +0 -1
  182. nautobot/dcim/migrations/0014_location_status_data_migration.py +0 -1
  183. nautobot/dcim/migrations/0015_device_components__changeloggedmodel.py +0 -1
  184. nautobot/dcim/migrations/0016_device_components__timestamp_data_migration.py +0 -1
  185. nautobot/dcim/migrations/0017_locationtype_nestable.py +0 -1
  186. nautobot/dcim/migrations/0018_device_redundancy_group.py +0 -1
  187. nautobot/dcim/migrations/0019_device_redundancy_group_data_migration.py +0 -1
  188. nautobot/dcim/migrations/0020_move_site_fields_to_location_model.py +0 -1
  189. nautobot/dcim/migrations/0021_mptt_to_tree_queries.py +0 -1
  190. nautobot/dcim/migrations/0022_interface_mac_address_data_migration.py +0 -1
  191. nautobot/dcim/migrations/0023_alter_interface_mac_address.py +0 -1
  192. nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -2
  193. nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -14
  194. nautobot/dcim/migrations/0026_rename_device_and_rack_role.py +0 -1
  195. nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -2
  196. nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -2
  197. nautobot/dcim/migrations/0029_add_tree_managers_and_foreign_keys_pre_data_migration.py +0 -1
  198. nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
  199. nautobot/dcim/migrations/0031_rename_path_end_point_related_name.py +0 -1
  200. nautobot/dcim/migrations/0032_remove_site_foreign_key_from_dcim_models.py +0 -1
  201. nautobot/dcim/migrations/0033_created_datetime.py +0 -1
  202. nautobot/dcim/migrations/0034_fixup_fks_and_related_names.py +0 -1
  203. nautobot/dcim/migrations/0035_related_name_changes.py +1 -2
  204. nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -2
  205. nautobot/dcim/migrations/0037_interface_ip_addresses_m2m.py +0 -1
  206. nautobot/dcim/migrations/0038_alter_location_managers.py +0 -1
  207. nautobot/dcim/migrations/0039_remove_slug.py +24 -0
  208. nautobot/dcim/migrations/0040_tagsfield.py +109 -0
  209. nautobot/dcim/migrations/0041_ipam__namespaces.py +25 -0
  210. nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
  211. nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
  212. nautobot/dcim/models/cables.py +4 -35
  213. nautobot/dcim/models/device_component_templates.py +7 -2
  214. nautobot/dcim/models/device_components.py +26 -203
  215. nautobot/dcim/models/devices.py +30 -152
  216. nautobot/dcim/models/locations.py +3 -64
  217. nautobot/dcim/models/power.py +3 -51
  218. nautobot/dcim/models/racks.py +7 -86
  219. nautobot/dcim/navigation.py +141 -467
  220. nautobot/dcim/signals.py +0 -2
  221. nautobot/dcim/tables/devices.py +8 -5
  222. nautobot/dcim/tables/devicetypes.py +1 -1
  223. nautobot/dcim/tables/locations.py +2 -2
  224. nautobot/dcim/tables/power.py +2 -2
  225. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
  226. nautobot/dcim/templates/dcim/device.html +15 -4
  227. nautobot/dcim/templates/dcim/device_edit.html +6 -0
  228. nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +0 -1
  229. nautobot/dcim/templates/dcim/devicetype.html +2 -2
  230. nautobot/dcim/templates/dcim/interface.html +4 -0
  231. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
  232. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  233. nautobot/dcim/templates/dcim/location.html +16 -1
  234. nautobot/dcim/templates/dcim/locationtype.html +15 -0
  235. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
  236. nautobot/dcim/templates/dcim/rackgroup.html +0 -12
  237. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  238. nautobot/dcim/tests/test_api.py +202 -130
  239. nautobot/dcim/tests/test_cablepaths.py +47 -42
  240. nautobot/dcim/tests/test_filters.py +156 -134
  241. nautobot/dcim/tests/test_forms.py +12 -213
  242. nautobot/dcim/tests/test_graphql.py +8 -3
  243. nautobot/dcim/tests/test_migrations.py +6 -11
  244. nautobot/dcim/tests/test_models.py +208 -158
  245. nautobot/dcim/tests/test_natural_ordering.py +12 -14
  246. nautobot/dcim/tests/test_signals.py +7 -4
  247. nautobot/dcim/tests/test_views.py +270 -264
  248. nautobot/dcim/urls.py +21 -26
  249. nautobot/dcim/views.py +14 -156
  250. nautobot/docs/additional-features/caching.md +6 -87
  251. nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
  252. nautobot/docs/additional-features/jobs.md +179 -197
  253. nautobot/docs/administration/nautobot-server.md +9 -24
  254. nautobot/docs/administration/nautobot-shell.md +6 -6
  255. nautobot/docs/administration/replicating-nautobot.md +0 -10
  256. nautobot/docs/configuration/index.md +9 -9
  257. nautobot/docs/configuration/optional-settings.md +32 -61
  258. nautobot/docs/configuration/required-settings.md +11 -52
  259. nautobot/docs/development/application-registry.md +2 -13
  260. nautobot/docs/development/best-practices.md +2 -1
  261. nautobot/docs/development/docker-compose-advanced-use-cases.md +1 -1
  262. nautobot/docs/development/extending-models.md +15 -17
  263. nautobot/docs/development/generic-views.md +0 -2
  264. nautobot/docs/development/getting-started.md +56 -6
  265. nautobot/docs/development/navigation-menu.md +22 -93
  266. nautobot/docs/development/react-ui.md +105 -0
  267. nautobot/docs/development/release-checklist.md +3 -3
  268. nautobot/docs/development/role-internals.md +1 -3
  269. nautobot/docs/development/style-guide.md +6 -4
  270. nautobot/docs/development/templates.md +2 -1
  271. nautobot/docs/docker/index.md +16 -14
  272. nautobot/docs/index.md +7 -3
  273. nautobot/docs/installation/index.md +4 -1
  274. nautobot/docs/installation/migrating-from-netbox.md +12 -43
  275. nautobot/docs/installation/migrating-from-postgresql.md +1 -1
  276. nautobot/docs/installation/nautobot.md +1 -1
  277. nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  278. nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  279. nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  280. nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  281. nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
  282. nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  283. nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  284. nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  285. nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  286. nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  287. nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  288. nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  289. nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  290. nautobot/docs/installation/upgrading-from-nautobot-v1.md +190 -636
  291. nautobot/docs/installation/upgrading.md +5 -2
  292. nautobot/docs/models/dcim/device.md +3 -0
  293. nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
  294. nautobot/docs/models/extras/computedfield.md +4 -4
  295. nautobot/docs/models/extras/dynamicgroup.md +9 -9
  296. nautobot/docs/models/extras/gitrepository.md +3 -0
  297. nautobot/docs/models/extras/job.md +1 -0
  298. nautobot/docs/models/extras/jobbutton.md +18 -13
  299. nautobot/docs/models/extras/jobhook.md +7 -4
  300. nautobot/docs/models/extras/jobresult.md +6 -2
  301. nautobot/docs/models/extras/relationship.md +2 -2
  302. nautobot/docs/models/extras/status.md +6 -19
  303. nautobot/docs/models/ipam/ipaddress.md +3 -0
  304. nautobot/docs/models/ipam/vrf.md +0 -3
  305. nautobot/docs/models/virtualization/virtualmachine.md +3 -0
  306. nautobot/docs/plugins/development.md +92 -24
  307. nautobot/docs/release-notes/version-1.5.md +96 -0
  308. nautobot/docs/release-notes/version-2.0.md +216 -0
  309. nautobot/docs/requirements.txt +5 -4
  310. nautobot/docs/rest-api/overview.md +384 -215
  311. nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
  312. nautobot/extras/admin.py +3 -5
  313. nautobot/extras/api/customfields.py +15 -39
  314. nautobot/extras/api/fields.py +0 -11
  315. nautobot/extras/api/mixins.py +45 -0
  316. nautobot/extras/api/relationships.py +63 -159
  317. nautobot/extras/api/serializers.py +165 -706
  318. nautobot/extras/api/urls.py +1 -1
  319. nautobot/extras/api/views.py +295 -282
  320. nautobot/extras/apps.py +4 -7
  321. nautobot/extras/choices.py +11 -22
  322. nautobot/extras/constants.py +9 -3
  323. nautobot/extras/datasources/__init__.py +2 -0
  324. nautobot/extras/datasources/git.py +135 -186
  325. nautobot/extras/datasources/registry.py +25 -35
  326. nautobot/extras/factory.py +1 -3
  327. nautobot/extras/filters/__init__.py +49 -47
  328. nautobot/extras/filters/mixins.py +10 -8
  329. nautobot/extras/forms/forms.py +72 -148
  330. nautobot/extras/forms/mixins.py +34 -57
  331. nautobot/extras/health_checks.py +0 -33
  332. nautobot/extras/jobs.py +387 -566
  333. nautobot/extras/management/__init__.py +55 -48
  334. nautobot/extras/management/commands/renaturalize.py +0 -1
  335. nautobot/extras/management/commands/runjob.py +24 -62
  336. nautobot/extras/management/commands/webhook_receiver.py +0 -1
  337. nautobot/extras/managers.py +30 -7
  338. nautobot/extras/migrations/0001_initial_part_1.py +0 -1
  339. nautobot/extras/migrations/0002_initial_part_2.py +0 -1
  340. nautobot/extras/migrations/0003_initial_part_3.py +0 -1
  341. nautobot/extras/migrations/0004_populate_default_status_records.py +0 -1
  342. nautobot/extras/migrations/0005_configcontext_device_types.py +0 -1
  343. nautobot/extras/migrations/0006_graphqlquery.py +0 -1
  344. nautobot/extras/migrations/0007_configcontextschema.py +0 -1
  345. nautobot/extras/migrations/0008_jobresult__custom_field_data.py +0 -1
  346. nautobot/extras/migrations/0009_computedfield.py +0 -1
  347. nautobot/extras/migrations/0010_change_cf_validation_max_min_field_to_bigint.py +0 -1
  348. nautobot/extras/migrations/0011_fileattachment_fileproxy.py +0 -1
  349. nautobot/extras/migrations/0012_healthchecktestmodel.py +0 -1
  350. nautobot/extras/migrations/0013_default_fallback_value_computedfield.py +0 -1
  351. nautobot/extras/migrations/0014_auto_slug.py +0 -1
  352. nautobot/extras/migrations/0015_scheduled_job.py +0 -1
  353. nautobot/extras/migrations/0016_secret.py +0 -1
  354. nautobot/extras/migrations/0017_joblogentry.py +0 -1
  355. nautobot/extras/migrations/0018_joblog_data_migration.py +0 -2
  356. nautobot/extras/migrations/0019_joblogentry__meta_options__related_name.py +0 -1
  357. nautobot/extras/migrations/0020_customfield_changelog.py +0 -1
  358. nautobot/extras/migrations/0021_customfield_changelog_data.py +0 -1
  359. nautobot/extras/migrations/0022_objectchange_object_datav2.py +0 -1
  360. nautobot/extras/migrations/0023_job_model.py +0 -1
  361. nautobot/extras/migrations/0024_job_data_migration.py +0 -1
  362. nautobot/extras/migrations/0025_add_advanced_ui_boolean_to_customfield_conputedfield_and_relationship.py +0 -1
  363. nautobot/extras/migrations/0026_job_add_gitrepository_fk.py +0 -1
  364. nautobot/extras/migrations/0027_job_gitrepository_data_migration.py +0 -1
  365. nautobot/extras/migrations/0028_job_reduce_source.py +0 -1
  366. nautobot/extras/migrations/0029_dynamicgroup.py +0 -1
  367. nautobot/extras/migrations/0030_webhook_alter_unique_together.py +0 -1
  368. nautobot/extras/migrations/0031_tag_content_types.py +0 -1
  369. nautobot/extras/migrations/0032_tag_content_types_data_migration.py +0 -1
  370. nautobot/extras/migrations/0033_add__optimized_indexing.py +0 -1
  371. nautobot/extras/migrations/0034_alter_fileattachment_mimetype.py +0 -1
  372. nautobot/extras/migrations/0035_scheduledjob_crontab.py +0 -1
  373. nautobot/extras/migrations/0036_job_add_has_sensitive_variables.py +0 -1
  374. nautobot/extras/migrations/0037_configcontextschema__remove_name_unique__create_constraint_unique_name_owner.py +0 -1
  375. nautobot/extras/migrations/0038_configcontext_locations.py +0 -1
  376. nautobot/extras/migrations/0039_objectchange__add_change_context.py +0 -1
  377. nautobot/extras/migrations/0040_dynamicgroup__dynamicgroupmembership.py +0 -1
  378. nautobot/extras/migrations/0041_jobresult_job_kwargs.py +0 -1
  379. nautobot/extras/migrations/0042_job__add_is_job_hook_receiver.py +0 -1
  380. nautobot/extras/migrations/0043_note.py +0 -1
  381. nautobot/extras/migrations/0044_add_job_hook.py +0 -1
  382. nautobot/extras/migrations/0045_add_custom_field_slug.py +0 -1
  383. nautobot/extras/migrations/0046_populate_custom_field_slug_label.py +0 -1
  384. nautobot/extras/migrations/0047_enforce_custom_field_slug.py +0 -1
  385. nautobot/extras/migrations/0048_alter_objectchange_change_context_detail.py +0 -1
  386. nautobot/extras/migrations/0049_alter_tag_slug.py +0 -1
  387. nautobot/extras/migrations/0050_customfield_grouping.py +0 -1
  388. nautobot/extras/migrations/0051_add_job_task_queues.py +0 -1
  389. nautobot/extras/migrations/0052_configcontext_device_redundancy_groups.py +0 -1
  390. nautobot/extras/migrations/0053_relationship_required_on.py +0 -1
  391. nautobot/extras/migrations/0054_scheduledjob_kwargs_request_user_change.py +0 -1
  392. nautobot/extras/migrations/0055_configcontext_dynamic_groups.py +0 -1
  393. nautobot/extras/migrations/0056_objectchange_add_reverse_time_idx.py +0 -1
  394. nautobot/extras/migrations/0057_jobbutton.py +0 -1
  395. nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
  396. nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -2
  397. nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -2
  398. nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -8
  399. nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -33
  400. nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -2
  401. nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -2
  402. nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
  403. nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -2
  404. nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -3
  405. nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -2
  406. nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -2
  407. nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
  408. nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -2
  409. nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
  410. nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -10
  411. nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -2
  412. nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
  413. nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
  414. nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
  415. nautobot/extras/migrations/0078_remove_slug.py +45 -0
  416. nautobot/extras/migrations/0079_tagsfield.py +28 -0
  417. nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
  418. nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
  419. nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
  420. nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
  421. nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
  422. nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
  423. nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
  424. nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
  425. nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
  426. nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
  427. nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
  428. nautobot/extras/models/__init__.py +2 -3
  429. nautobot/extras/models/change_logging.py +0 -36
  430. nautobot/extras/models/customfields.py +39 -33
  431. nautobot/extras/models/datasources.py +48 -50
  432. nautobot/extras/models/groups.py +5 -12
  433. nautobot/extras/models/jobs.py +190 -323
  434. nautobot/extras/models/mixins.py +0 -71
  435. nautobot/extras/models/models.py +1 -22
  436. nautobot/extras/models/relationships.py +20 -21
  437. nautobot/extras/models/roles.py +0 -23
  438. nautobot/extras/models/secrets.py +2 -31
  439. nautobot/extras/models/statuses.py +6 -5
  440. nautobot/extras/models/tags.py +2 -17
  441. nautobot/extras/navigation.py +89 -307
  442. nautobot/extras/plugins/__init__.py +3 -121
  443. nautobot/extras/plugins/utils.py +0 -3
  444. nautobot/extras/plugins/validators.py +5 -4
  445. nautobot/extras/plugins/views.py +16 -4
  446. nautobot/extras/querysets.py +1 -7
  447. nautobot/extras/registry.py +3 -0
  448. nautobot/extras/signals.py +26 -60
  449. nautobot/extras/tables.py +42 -49
  450. nautobot/extras/tasks.py +0 -12
  451. nautobot/extras/templates/extras/configcontext.html +1 -1
  452. nautobot/extras/templates/extras/configcontextschema.html +16 -1
  453. nautobot/extras/templates/extras/customfield.html +0 -13
  454. nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -1
  455. nautobot/extras/templates/extras/gitrepository.html +3 -3
  456. nautobot/extras/templates/extras/inc/jobresult.html +10 -0
  457. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  458. nautobot/extras/templates/extras/job.html +35 -25
  459. nautobot/extras/templates/extras/job_approval_request.html +15 -30
  460. nautobot/extras/templates/extras/job_detail.html +13 -31
  461. nautobot/extras/templates/extras/job_edit.html +14 -17
  462. nautobot/extras/templates/extras/jobresult.html +24 -6
  463. nautobot/extras/templates/extras/objectchange_list.html +1 -1
  464. nautobot/extras/templates/extras/scheduledjob.html +2 -2
  465. nautobot/extras/templates/extras/secret.html +28 -0
  466. nautobot/extras/templates/extras/secret_edit.html +0 -1
  467. nautobot/extras/templates/extras/secretsgroup_edit.html +0 -1
  468. nautobot/extras/templatetags/custom_links.py +0 -2
  469. nautobot/extras/templatetags/job_buttons.py +1 -0
  470. nautobot/extras/templatetags/plugins.py +0 -1
  471. nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
  472. nautobot/extras/test_jobs/atomic_transaction.py +53 -0
  473. nautobot/extras/test_jobs/dry_run.py +29 -0
  474. nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
  475. nautobot/extras/test_jobs/duplicate_name2.py +9 -0
  476. nautobot/extras/test_jobs/fail.py +23 -0
  477. nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
  478. nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
  479. nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
  480. nautobot/extras/test_jobs/file_upload_pass.py +25 -0
  481. nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
  482. nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
  483. nautobot/extras/test_jobs/job_button_receiver.py +28 -0
  484. nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
  485. nautobot/extras/test_jobs/job_variables.py +88 -0
  486. nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
  487. nautobot/extras/test_jobs/log_redaction.py +20 -0
  488. nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
  489. nautobot/extras/test_jobs/modify_db.py +25 -0
  490. nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
  491. nautobot/extras/test_jobs/object_var_optional.py +21 -0
  492. nautobot/extras/test_jobs/object_var_required.py +21 -0
  493. nautobot/extras/test_jobs/object_vars.py +26 -0
  494. nautobot/extras/test_jobs/pass.py +25 -0
  495. nautobot/extras/test_jobs/profiling.py +32 -0
  496. nautobot/extras/test_jobs/read_only_job.py +15 -0
  497. nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
  498. nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
  499. nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
  500. nautobot/extras/tests/integration/__init__.py +3 -3
  501. nautobot/extras/tests/integration/test_computedfields.py +1 -1
  502. nautobot/extras/tests/integration/test_configcontextschema.py +7 -5
  503. nautobot/extras/tests/integration/test_customfields.py +4 -2
  504. nautobot/extras/tests/integration/test_dynamicgroups.py +2 -2
  505. nautobot/extras/tests/integration/test_jobs.py +25 -27
  506. nautobot/extras/tests/integration/test_notes.py +8 -4
  507. nautobot/extras/tests/integration/test_plugins.py +4 -4
  508. nautobot/extras/tests/integration/test_relationships.py +2 -2
  509. nautobot/extras/tests/test_api.py +371 -381
  510. nautobot/extras/tests/test_changelog.py +17 -16
  511. nautobot/extras/tests/test_context_managers.py +5 -6
  512. nautobot/extras/tests/test_customfields.py +112 -73
  513. nautobot/extras/tests/test_datasources.py +191 -117
  514. nautobot/extras/tests/test_dynamicgroups.py +45 -68
  515. nautobot/extras/tests/test_filters.py +170 -130
  516. nautobot/extras/tests/test_forms.py +107 -109
  517. nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
  518. nautobot/extras/tests/test_jobs.py +271 -273
  519. nautobot/extras/tests/test_management.py +3 -6
  520. nautobot/extras/tests/test_migrations.py +5 -3
  521. nautobot/extras/tests/test_models.py +121 -173
  522. nautobot/extras/tests/test_notes.py +0 -1
  523. nautobot/extras/tests/test_plugins.py +55 -89
  524. nautobot/extras/tests/test_relationships.py +174 -130
  525. nautobot/extras/tests/test_tags.py +6 -12
  526. nautobot/extras/tests/test_utils.py +31 -1
  527. nautobot/extras/tests/test_views.py +223 -184
  528. nautobot/extras/tests/test_webhooks.py +16 -15
  529. nautobot/extras/urls.py +69 -69
  530. nautobot/extras/utils.py +137 -163
  531. nautobot/extras/views.py +81 -153
  532. nautobot/ipam/api/fields.py +17 -0
  533. nautobot/ipam/api/serializers.py +77 -164
  534. nautobot/ipam/api/urls.py +4 -1
  535. nautobot/ipam/api/views.py +28 -19
  536. nautobot/ipam/apps.py +1 -0
  537. nautobot/ipam/choices.py +5 -12
  538. nautobot/ipam/constants.py +1 -0
  539. nautobot/ipam/factory.py +41 -30
  540. nautobot/ipam/filters.py +58 -25
  541. nautobot/ipam/forms.py +82 -211
  542. nautobot/ipam/graphql/types.py +0 -9
  543. nautobot/ipam/lookups.py +13 -8
  544. nautobot/ipam/management/commands/__init__.py +0 -0
  545. nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
  546. nautobot/ipam/migrations/0001_initial_part_1.py +0 -1
  547. nautobot/ipam/migrations/0002_initial_part_2.py +0 -1
  548. nautobot/ipam/migrations/0003_remove_max_length.py +0 -1
  549. nautobot/ipam/migrations/0004_fixup_p2p_broadcast.py +0 -1
  550. nautobot/ipam/migrations/0005_auto_slug.py +0 -1
  551. nautobot/ipam/migrations/0006_ipaddress_nat_outside_list.py +0 -1
  552. nautobot/ipam/migrations/0007_add_natural_indexing.py +0 -1
  553. nautobot/ipam/migrations/0008_prefix_vlan_vlangroup_location.py +0 -1
  554. nautobot/ipam/migrations/0009_alter_vlan_name.py +0 -1
  555. nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -2
  556. nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -39
  557. nautobot/ipam/migrations/0012_rename_ipam_roles.py +0 -1
  558. nautobot/ipam/migrations/0013_delete_role.py +0 -1
  559. nautobot/ipam/migrations/0014_rename_foreign_keys_and_related_names.py +0 -1
  560. nautobot/ipam/migrations/0015_prefix_add_type.py +0 -1
  561. nautobot/ipam/migrations/0016_prefix_type_data_migration.py +0 -3
  562. nautobot/ipam/migrations/0017_prefix_remove_is_pool.py +0 -1
  563. nautobot/ipam/migrations/0018_remove_site_foreign_key_from_ipam_models.py +0 -1
  564. nautobot/ipam/migrations/0019_created_datetime.py +0 -1
  565. nautobot/ipam/migrations/0020_related_name_changes.py +1 -2
  566. nautobot/ipam/migrations/0021_prefix_add_rir_and_date_allocated.py +0 -1
  567. nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +3 -5
  568. nautobot/ipam/migrations/0023_delete_aggregate.py +0 -1
  569. nautobot/ipam/migrations/0024_interface_to_ipaddress_m2m.py +0 -1
  570. nautobot/ipam/migrations/0025_interface_ipaddress_m2m_data_migration.py +0 -1
  571. nautobot/ipam/migrations/0026_ipaddress_remove_assigned_object.py +0 -1
  572. nautobot/ipam/migrations/0027_remove_rir_slug.py +16 -0
  573. nautobot/ipam/migrations/0028_tagsfield.py +44 -0
  574. nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
  575. nautobot/ipam/migrations/0030_ipam__namespaces.py +231 -0
  576. nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
  577. nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
  578. nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
  579. nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
  580. nautobot/ipam/models.py +579 -368
  581. nautobot/ipam/navigation.py +36 -159
  582. nautobot/ipam/querysets.py +117 -90
  583. nautobot/ipam/signals.py +89 -0
  584. nautobot/ipam/tables.py +86 -28
  585. nautobot/ipam/templates/ipam/ipaddress.html +14 -30
  586. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -0
  587. nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
  588. nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
  589. nautobot/ipam/templates/ipam/namespace_retrieve.html +42 -0
  590. nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
  591. nautobot/ipam/templates/ipam/prefix.html +27 -33
  592. nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
  593. nautobot/ipam/templates/ipam/vlangroup.html +0 -13
  594. nautobot/ipam/templates/ipam/vrf.html +6 -4
  595. nautobot/ipam/templates/ipam/vrf_edit.html +20 -2
  596. nautobot/ipam/tests/integration/test_prefixes.py +4 -27
  597. nautobot/ipam/tests/test_api.py +60 -61
  598. nautobot/ipam/tests/test_filters.py +187 -126
  599. nautobot/ipam/tests/test_forms.py +12 -6
  600. nautobot/ipam/tests/test_graphql.py +8 -6
  601. nautobot/ipam/tests/test_migrations.py +8 -13
  602. nautobot/ipam/tests/test_models.py +426 -274
  603. nautobot/ipam/tests/test_ordering.py +6 -3
  604. nautobot/ipam/tests/test_querysets.py +340 -96
  605. nautobot/ipam/tests/test_views.py +100 -55
  606. nautobot/ipam/urls.py +28 -5
  607. nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
  608. nautobot/ipam/utils/migrations.py +713 -0
  609. nautobot/ipam/views.py +237 -122
  610. nautobot/project-static/docs/404.html +1399 -166
  611. nautobot/project-static/docs/additional-features/caching.html +1416 -320
  612. nautobot/project-static/docs/additional-features/change-logging.html +1389 -190
  613. nautobot/project-static/docs/additional-features/config-contexts.html +1389 -190
  614. nautobot/project-static/docs/additional-features/graphql.html +1389 -190
  615. nautobot/project-static/docs/additional-features/healthcheck.html +1389 -190
  616. nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1393 -190
  617. nautobot/project-static/docs/additional-features/jobs.html +1677 -460
  618. nautobot/project-static/docs/additional-features/napalm.html +1389 -190
  619. nautobot/project-static/docs/additional-features/prometheus-metrics.html +1389 -190
  620. nautobot/project-static/docs/additional-features/template-filters.html +1389 -190
  621. nautobot/project-static/docs/administration/celery-queues.html +1389 -190
  622. nautobot/project-static/docs/administration/nautobot-server.html +1553 -375
  623. nautobot/project-static/docs/administration/nautobot-shell.html +1395 -196
  624. nautobot/project-static/docs/administration/permissions.html +1389 -190
  625. nautobot/project-static/docs/administration/replicating-nautobot.html +1387 -207
  626. nautobot/project-static/docs/apps/index.html +1389 -190
  627. nautobot/project-static/docs/apps/nautobot-apps.html +1387 -175
  628. nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js +29 -0
  629. nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js.map +8 -0
  630. nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js → search.208ed371.min.js} +9 -15
  631. nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js.map → search.208ed371.min.js.map} +4 -4
  632. nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css +1 -0
  633. nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css.map +1 -0
  634. nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css +1 -0
  635. nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css.map +1 -0
  636. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1775 -590
  637. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1389 -190
  638. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3588 -1922
  639. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1461 -262
  640. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1401 -170
  641. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1396 -191
  642. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +2095 -894
  643. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2357 -1194
  644. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2258 -940
  645. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1389 -190
  646. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1400 -201
  647. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +11068 -7861
  648. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2867 -2224
  649. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1389 -190
  650. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2641 -1573
  651. nautobot/project-static/docs/configuration/authentication/ldap.html +1389 -190
  652. nautobot/project-static/docs/configuration/authentication/remote.html +1389 -190
  653. nautobot/project-static/docs/configuration/authentication/sso.html +1389 -190
  654. nautobot/project-static/docs/configuration/index.html +1398 -199
  655. nautobot/project-static/docs/configuration/optional-settings.html +1418 -274
  656. nautobot/project-static/docs/configuration/required-settings.html +1419 -287
  657. nautobot/project-static/docs/core-functionality/circuits.html +1446 -247
  658. nautobot/project-static/docs/core-functionality/device-types.html +1448 -249
  659. nautobot/project-static/docs/core-functionality/devices.html +1452 -249
  660. nautobot/project-static/docs/core-functionality/ipam.html +1452 -253
  661. nautobot/project-static/docs/core-functionality/power.html +1448 -249
  662. nautobot/project-static/docs/core-functionality/secrets.html +1448 -249
  663. nautobot/project-static/docs/core-functionality/services.html +1448 -249
  664. nautobot/project-static/docs/core-functionality/sites-and-racks.html +1448 -249
  665. nautobot/project-static/docs/core-functionality/tenancy.html +1448 -249
  666. nautobot/project-static/docs/core-functionality/virtualization.html +1452 -249
  667. nautobot/project-static/docs/core-functionality/vlans.html +1448 -249
  668. nautobot/project-static/docs/development/application-registry.html +1393 -214
  669. nautobot/project-static/docs/development/best-practices.html +1392 -192
  670. nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1390 -191
  671. nautobot/project-static/docs/development/extending-models.html +1443 -257
  672. nautobot/project-static/docs/development/generic-views.html +1403 -174
  673. nautobot/project-static/docs/development/getting-started.html +1568 -262
  674. nautobot/project-static/docs/development/homepage.html +1389 -190
  675. nautobot/project-static/docs/development/index.html +1389 -190
  676. nautobot/project-static/docs/development/model-features.html +1389 -190
  677. nautobot/project-static/docs/development/natural-keys.html +1389 -190
  678. nautobot/project-static/docs/development/navigation-menu.html +1451 -330
  679. nautobot/project-static/docs/development/react-ui.html +4199 -0
  680. nautobot/project-static/docs/development/release-checklist.html +1392 -193
  681. nautobot/project-static/docs/development/role-internals.html +1402 -172
  682. nautobot/project-static/docs/development/style-guide.html +1399 -199
  683. nautobot/project-static/docs/development/templates.html +1391 -191
  684. nautobot/project-static/docs/development/testing.html +1389 -190
  685. nautobot/project-static/docs/development/user-preferences.html +1389 -190
  686. nautobot/project-static/docs/docker/index.html +1408 -206
  687. nautobot/project-static/docs/index.html +1397 -180
  688. nautobot/project-static/docs/installation/centos.html +1401 -170
  689. nautobot/project-static/docs/installation/external-authentication.html +1389 -190
  690. nautobot/project-static/docs/installation/http-server.html +1389 -190
  691. nautobot/project-static/docs/installation/index.html +1394 -191
  692. nautobot/project-static/docs/installation/migrating-from-netbox.html +1452 -305
  693. nautobot/project-static/docs/installation/migrating-from-postgresql.html +1390 -191
  694. nautobot/project-static/docs/installation/nautobot.html +1390 -191
  695. nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1389 -190
  696. nautobot/project-static/docs/installation/selinux-troubleshooting.html +1401 -170
  697. nautobot/project-static/docs/installation/services.html +1389 -190
  698. nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  699. nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  700. nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  701. nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  702. nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
  703. nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  704. nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  705. nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  706. nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  707. nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  708. nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  709. nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  710. nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  711. nautobot/project-static/docs/installation/ubuntu.html +1401 -170
  712. nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +4254 -1923
  713. nautobot/project-static/docs/installation/upgrading.html +1395 -192
  714. nautobot/project-static/docs/models/circuits/circuit.html +1427 -174
  715. nautobot/project-static/docs/models/circuits/circuittermination.html +1427 -174
  716. nautobot/project-static/docs/models/circuits/circuittype.html +1427 -174
  717. nautobot/project-static/docs/models/circuits/provider.html +1427 -174
  718. nautobot/project-static/docs/models/circuits/providernetwork.html +1427 -174
  719. nautobot/project-static/docs/models/dcim/cable.html +1458 -174
  720. nautobot/project-static/docs/models/dcim/consoleport.html +1427 -174
  721. nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1427 -174
  722. nautobot/project-static/docs/models/dcim/consoleserverport.html +1427 -174
  723. nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1427 -174
  724. nautobot/project-static/docs/models/dcim/device.html +1431 -174
  725. nautobot/project-static/docs/models/dcim/devicebay.html +1427 -174
  726. nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1427 -174
  727. nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1522 -177
  728. nautobot/project-static/docs/models/dcim/devicetype.html +1427 -174
  729. nautobot/project-static/docs/models/dcim/frontport.html +1427 -174
  730. nautobot/project-static/docs/models/dcim/frontporttemplate.html +1427 -174
  731. nautobot/project-static/docs/models/dcim/interface.html +1427 -174
  732. nautobot/project-static/docs/models/dcim/interfacetemplate.html +1427 -174
  733. nautobot/project-static/docs/models/dcim/inventoryitem.html +1427 -174
  734. nautobot/project-static/docs/models/dcim/location.html +1427 -174
  735. nautobot/project-static/docs/models/dcim/locationtype.html +1427 -174
  736. nautobot/project-static/docs/models/dcim/manufacturer.html +1427 -174
  737. nautobot/project-static/docs/models/dcim/platform.html +1427 -174
  738. nautobot/project-static/docs/models/dcim/powerfeed.html +1425 -172
  739. nautobot/project-static/docs/models/dcim/poweroutlet.html +1427 -174
  740. nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1427 -174
  741. nautobot/project-static/docs/models/dcim/powerpanel.html +1425 -172
  742. nautobot/project-static/docs/models/dcim/powerport.html +1427 -174
  743. nautobot/project-static/docs/models/dcim/powerporttemplate.html +1427 -174
  744. nautobot/project-static/docs/models/dcim/rack.html +1427 -174
  745. nautobot/project-static/docs/models/dcim/rackgroup.html +1427 -174
  746. nautobot/project-static/docs/models/dcim/rackreservation.html +1427 -174
  747. nautobot/project-static/docs/models/dcim/rearport.html +1427 -174
  748. nautobot/project-static/docs/models/dcim/rearporttemplate.html +1427 -174
  749. nautobot/project-static/docs/models/dcim/region.html +1401 -170
  750. nautobot/project-static/docs/models/dcim/site.html +1401 -170
  751. nautobot/project-static/docs/models/dcim/virtualchassis.html +1425 -172
  752. nautobot/project-static/docs/models/extras/computedfield.html +1393 -194
  753. nautobot/project-static/docs/models/extras/configcontext.html +1465 -174
  754. nautobot/project-static/docs/models/extras/configcontextschema.html +1421 -168
  755. nautobot/project-static/docs/models/extras/customfield.html +1389 -190
  756. nautobot/project-static/docs/models/extras/customlink.html +1389 -190
  757. nautobot/project-static/docs/models/extras/dynamicgroup.html +1398 -199
  758. nautobot/project-static/docs/models/extras/exporttemplate.html +1389 -190
  759. nautobot/project-static/docs/models/extras/gitrepository.html +1393 -190
  760. nautobot/project-static/docs/models/extras/graphqlquery.html +1469 -171
  761. nautobot/project-static/docs/models/extras/imageattachment.html +1434 -181
  762. nautobot/project-static/docs/models/extras/job.html +1411 -157
  763. nautobot/project-static/docs/models/extras/jobbutton.html +1410 -207
  764. nautobot/project-static/docs/models/extras/jobhook.html +1397 -194
  765. nautobot/project-static/docs/models/extras/joblogentry.html +1408 -155
  766. nautobot/project-static/docs/models/extras/jobresult.html +1417 -159
  767. nautobot/project-static/docs/models/extras/note.html +1389 -190
  768. nautobot/project-static/docs/models/extras/relationship.html +1391 -192
  769. nautobot/project-static/docs/models/extras/role.html +1495 -198
  770. nautobot/project-static/docs/models/extras/secret.html +1492 -201
  771. nautobot/project-static/docs/models/extras/secretsgroup.html +1410 -157
  772. nautobot/project-static/docs/models/extras/status.html +1381 -221
  773. nautobot/project-static/docs/models/extras/tag.html +1389 -190
  774. nautobot/project-static/docs/models/extras/webhook.html +1389 -190
  775. nautobot/project-static/docs/models/ipam/ipaddress.html +1488 -200
  776. nautobot/project-static/docs/models/ipam/prefix.html +1410 -157
  777. nautobot/project-static/docs/models/ipam/rir.html +1410 -157
  778. nautobot/project-static/docs/models/ipam/routetarget.html +1410 -157
  779. nautobot/project-static/docs/models/ipam/service.html +1410 -157
  780. nautobot/project-static/docs/models/ipam/vlan.html +1410 -157
  781. nautobot/project-static/docs/models/ipam/vlangroup.html +1410 -157
  782. nautobot/project-static/docs/models/ipam/vrf.html +1410 -161
  783. nautobot/project-static/docs/models/tenancy/tenant.html +1412 -159
  784. nautobot/project-static/docs/models/tenancy/tenantgroup.html +1412 -159
  785. nautobot/project-static/docs/models/users/objectpermission.html +1462 -171
  786. nautobot/project-static/docs/models/users/token.html +1410 -157
  787. nautobot/project-static/docs/models/virtualization/cluster.html +1410 -157
  788. nautobot/project-static/docs/models/virtualization/clustergroup.html +1410 -157
  789. nautobot/project-static/docs/models/virtualization/clustertype.html +1410 -157
  790. nautobot/project-static/docs/models/virtualization/virtualmachine.html +1414 -157
  791. nautobot/project-static/docs/models/virtualization/vminterface.html +1410 -157
  792. nautobot/project-static/docs/objects.inv +0 -0
  793. nautobot/project-static/docs/plugins/development.html +1916 -646
  794. nautobot/project-static/docs/plugins/index.html +1389 -190
  795. nautobot/project-static/docs/plugins/porting-from-netbox.html +1389 -190
  796. nautobot/project-static/docs/release-notes/index.html +1389 -190
  797. nautobot/project-static/docs/release-notes/version-1.0.html +1389 -190
  798. nautobot/project-static/docs/release-notes/version-1.1.html +1389 -190
  799. nautobot/project-static/docs/release-notes/version-1.2.html +1389 -190
  800. nautobot/project-static/docs/release-notes/version-1.3.html +1389 -190
  801. nautobot/project-static/docs/release-notes/version-1.4.html +1389 -190
  802. nautobot/project-static/docs/release-notes/version-1.5.html +2016 -397
  803. nautobot/project-static/docs/release-notes/version-2.0.html +1935 -287
  804. nautobot/project-static/docs/requirements.txt +5 -4
  805. nautobot/project-static/docs/rest-api/authentication.html +1389 -190
  806. nautobot/project-static/docs/rest-api/filtering.html +1389 -190
  807. nautobot/project-static/docs/rest-api/overview.html +2002 -576
  808. nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
  809. nautobot/project-static/docs/search/search_index.json +1 -1
  810. nautobot/project-static/docs/sitemap.xml +197 -187
  811. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  812. nautobot/project-static/docs/user-guides/custom-fields.html +1390 -191
  813. nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1392 -193
  814. nautobot/project-static/docs/user-guides/getting-started/index.html +1388 -189
  815. nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1388 -189
  816. nautobot/project-static/docs/user-guides/getting-started/ipam.html +1386 -187
  817. nautobot/project-static/docs/user-guides/getting-started/platforms.html +1448 -249
  818. nautobot/project-static/docs/user-guides/getting-started/regions.html +1411 -212
  819. nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1395 -196
  820. nautobot/project-static/docs/user-guides/getting-started/tenants.html +1448 -249
  821. nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1448 -249
  822. nautobot/project-static/docs/user-guides/git-data-source.html +1405 -206
  823. nautobot/project-static/docs/user-guides/graphql.html +1402 -203
  824. nautobot/project-static/docs/user-guides/relationships.html +1448 -249
  825. nautobot/project-static/docs/user-guides/s3-django-storage.html +1448 -249
  826. nautobot/project-static/js/forms.js +16 -9
  827. nautobot/project-static/js/theme.js +5 -0
  828. nautobot/tenancy/api/serializers.py +4 -34
  829. nautobot/tenancy/api/urls.py +1 -1
  830. nautobot/tenancy/filters/__init__.py +9 -7
  831. nautobot/tenancy/filters/mixins.py +3 -2
  832. nautobot/tenancy/forms.py +3 -36
  833. nautobot/tenancy/migrations/0001_initial.py +0 -1
  834. nautobot/tenancy/migrations/0002_auto_slug.py +0 -1
  835. nautobot/tenancy/migrations/0003_mptt_to_tree_queries.py +0 -1
  836. nautobot/tenancy/migrations/0004_change_tree_manager_on_tree_models.py +0 -1
  837. nautobot/tenancy/migrations/0005_rename_foreign_keys_and_related_names.py +0 -1
  838. nautobot/tenancy/migrations/0006_created_datetime.py +0 -1
  839. nautobot/tenancy/migrations/0007_remove_tenant_tenantgroup_slug.py +20 -0
  840. nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
  841. nautobot/tenancy/models.py +0 -30
  842. nautobot/tenancy/navigation.py +6 -39
  843. nautobot/tenancy/tables.py +4 -4
  844. nautobot/tenancy/templates/tenancy/tenant.html +12 -12
  845. nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -1
  846. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  847. nautobot/tenancy/tests/test_api.py +1 -12
  848. nautobot/tenancy/tests/test_filters.py +20 -12
  849. nautobot/tenancy/tests/test_views.py +11 -29
  850. nautobot/tenancy/urls.py +10 -10
  851. nautobot/tenancy/views.py +0 -3
  852. nautobot/ui/.eslintignore +6 -0
  853. nautobot/ui/.gitignore +10 -0
  854. nautobot/ui/.prettierignore +9 -0
  855. nautobot/ui/.prettierrc +4 -0
  856. nautobot/ui/README.md +33 -0
  857. nautobot/ui/app_imports.js.j2 +7 -0
  858. nautobot/ui/craco.config.js +46 -0
  859. nautobot/ui/jsconfig-base.json +11 -0
  860. nautobot/ui/jsconfig.json +5 -0
  861. nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
  862. nautobot/ui/package-lock.json +21451 -0
  863. nautobot/ui/package.json +70 -0
  864. nautobot/ui/public/index.html +47 -0
  865. nautobot/ui/public/logo192.png +0 -0
  866. nautobot/ui/public/logo512.png +0 -0
  867. nautobot/ui/public/manifest.json +25 -0
  868. nautobot/ui/public/nautobot_logo.svg +131 -0
  869. nautobot/ui/public/robots.txt +3 -0
  870. nautobot/ui/src/App.js +71 -0
  871. nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
  872. nautobot/ui/src/components/AppTab.js +40 -0
  873. nautobot/ui/src/components/Apps.js +60 -0
  874. nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
  875. nautobot/ui/src/components/HomePanel.js +58 -0
  876. nautobot/ui/src/components/JobHistoryTable.js +78 -0
  877. nautobot/ui/src/components/Layout.js +53 -0
  878. nautobot/ui/src/components/LoadingWidget.js +25 -0
  879. nautobot/ui/src/components/Navbar.js +116 -0
  880. nautobot/ui/src/components/NotificationPopover.js +27 -0
  881. nautobot/ui/src/components/ObjectListTable.js +209 -0
  882. nautobot/ui/src/components/ReferenceDataTag.js +35 -0
  883. nautobot/ui/src/components/RouterButton.js +10 -0
  884. nautobot/ui/src/components/RouterLink.js +10 -0
  885. nautobot/ui/src/components/SidebarNav.js +147 -0
  886. nautobot/ui/src/components/Table.js +48 -0
  887. nautobot/ui/src/components/TableItem.js +71 -0
  888. nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
  889. nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
  890. nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
  891. nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
  892. nautobot/ui/src/components/__tests__/Table.test.js +36 -0
  893. nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
  894. nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
  895. nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
  896. nautobot/ui/src/components/pagination.js +93 -0
  897. nautobot/ui/src/components/paginator.js +79 -0
  898. nautobot/ui/src/components/paginator_form.js +43 -0
  899. nautobot/ui/src/components/usePagination.js +57 -0
  900. nautobot/ui/src/constants/apiPath.js +10 -0
  901. nautobot/ui/src/constants/icons.js +15 -0
  902. nautobot/ui/src/constants/size.js +15 -0
  903. nautobot/ui/src/index.js +65 -0
  904. nautobot/ui/src/reportWebVitals.js +15 -0
  905. nautobot/ui/src/router.js +77 -0
  906. nautobot/ui/src/utils/api.js +131 -0
  907. nautobot/ui/src/utils/app-import.js +15 -0
  908. nautobot/ui/src/utils/color.js +15 -0
  909. nautobot/ui/src/utils/date.js +14 -0
  910. nautobot/ui/src/utils/index.js +15 -0
  911. nautobot/ui/src/utils/navigation.js +32 -0
  912. nautobot/ui/src/utils/session.js +64 -0
  913. nautobot/ui/src/utils/store.js +242 -0
  914. nautobot/ui/src/utils/string.js +6 -0
  915. nautobot/ui/src/utils/url.js +4 -0
  916. nautobot/ui/src/views/Home.js +138 -0
  917. nautobot/ui/src/views/InstalledApps.js +80 -0
  918. nautobot/ui/src/views/Login.js +48 -0
  919. nautobot/ui/src/views/Logout.js +20 -0
  920. nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
  921. nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
  922. nautobot/ui/src/views/__tests__/Login.test.js +15 -0
  923. nautobot/ui/src/views/generic/GenericView.js +142 -0
  924. nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
  925. nautobot/ui/src/views/generic/ObjectList.js +127 -0
  926. nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
  927. nautobot/users/admin.py +1 -1
  928. nautobot/users/api/serializers.py +51 -61
  929. nautobot/users/api/urls.py +1 -1
  930. nautobot/users/api/views.py +53 -2
  931. nautobot/users/migrations/0001_initial.py +0 -1
  932. nautobot/users/migrations/0002_token_ordering_by_created.py +0 -1
  933. nautobot/users/migrations/0003_alter_user_options.py +0 -1
  934. nautobot/users/migrations/0004_alter_user_managers.py +0 -1
  935. nautobot/users/tests/test_api.py +109 -28
  936. nautobot/users/tests/test_filters.py +0 -4
  937. nautobot/users/tests/test_models.py +0 -1
  938. nautobot/users/views.py +0 -7
  939. nautobot/virtualization/api/serializers.py +18 -132
  940. nautobot/virtualization/api/urls.py +1 -1
  941. nautobot/virtualization/api/views.py +1 -22
  942. nautobot/virtualization/choices.py +0 -2
  943. nautobot/virtualization/filters.py +12 -7
  944. nautobot/virtualization/forms.py +21 -117
  945. nautobot/virtualization/migrations/0001_initial.py +0 -1
  946. nautobot/virtualization/migrations/0002_virtualmachine_local_context_schema.py +0 -1
  947. nautobot/virtualization/migrations/0003_vminterface_verbose_name.py +0 -1
  948. nautobot/virtualization/migrations/0004_auto_slug.py +0 -1
  949. nautobot/virtualization/migrations/0005_add_natural_indexing.py +0 -1
  950. nautobot/virtualization/migrations/0006_vminterface_status.py +0 -1
  951. nautobot/virtualization/migrations/0007_vminterface_status_data_migration.py +0 -1
  952. nautobot/virtualization/migrations/0008_vminterface_parent.py +0 -1
  953. nautobot/virtualization/migrations/0009_cluster_location.py +0 -1
  954. nautobot/virtualization/migrations/0010_vminterface_mac_address_data_migration.py +0 -1
  955. nautobot/virtualization/migrations/0011_alter_vminterface_mac_address.py +0 -1
  956. nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -2
  957. nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -12
  958. nautobot/virtualization/migrations/0014_rename_virtualmachine_roles.py +0 -1
  959. nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -2
  960. nautobot/virtualization/migrations/0016_remove_site_foreign_key_from_cluster_class.py +0 -1
  961. nautobot/virtualization/migrations/0017_created_datetime.py +0 -1
  962. nautobot/virtualization/migrations/0018_related_name_changes.py +1 -2
  963. nautobot/virtualization/migrations/0019_vminterface_ip_addresses_m2m.py +0 -1
  964. nautobot/virtualization/migrations/0020_remove_clustergroup_clustertype_slug.py +20 -0
  965. nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
  966. nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
  967. nautobot/virtualization/migrations/0023_ipam__namespaces.py +25 -0
  968. nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
  969. nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
  970. nautobot/virtualization/models.py +39 -131
  971. nautobot/virtualization/navigation.py +18 -99
  972. nautobot/virtualization/tables.py +4 -4
  973. nautobot/virtualization/templates/virtualization/virtualmachine.html +13 -2
  974. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
  975. nautobot/virtualization/tests/test_api.py +42 -52
  976. nautobot/virtualization/tests/test_filters.py +98 -75
  977. nautobot/virtualization/tests/test_models.py +36 -13
  978. nautobot/virtualization/tests/test_views.py +68 -73
  979. nautobot/virtualization/urls.py +10 -10
  980. nautobot/virtualization/views.py +8 -14
  981. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +15 -22
  982. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +987 -834
  983. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
  984. nautobot/circuits/api/nested_serializers.py +0 -69
  985. nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
  986. nautobot/dcim/api/nested_serializers.py +0 -356
  987. nautobot/dcim/templates/dcim/device_import.html +0 -5
  988. nautobot/dcim/templates/dcim/device_import_child.html +0 -5
  989. nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
  990. nautobot/extras/api/nested_serializers.py +0 -353
  991. nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -42
  992. nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -47
  993. nautobot/extras/reports.py +0 -60
  994. nautobot/extras/scripts.py +0 -72
  995. nautobot/extras/tests/example_jobs/script_variables.py +0 -67
  996. nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
  997. nautobot/extras/tests/example_jobs/test_fail.py +0 -16
  998. nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
  999. nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
  1000. nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
  1001. nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
  1002. nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
  1003. nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
  1004. nautobot/extras/tests/example_jobs/test_modify_db.py +0 -19
  1005. nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
  1006. nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
  1007. nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
  1008. nautobot/extras/tests/example_jobs/test_pass.py +0 -19
  1009. nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
  1010. nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
  1011. nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
  1012. nautobot/ipam/api/nested_serializers.py +0 -143
  1013. nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js +0 -29
  1014. nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js.map +0 -8
  1015. nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js +0 -18
  1016. nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js.map +0 -8
  1017. nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css +0 -1
  1018. nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css.map +0 -1
  1019. nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css +0 -1
  1020. nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css.map +0 -1
  1021. nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css +0 -1
  1022. nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css.map +0 -1
  1023. nautobot/tenancy/api/nested_serializers.py +0 -31
  1024. nautobot/users/api/nested_serializers.py +0 -67
  1025. nautobot/virtualization/api/nested_serializers.py +0 -65
  1026. /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
  1027. /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
  1028. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
  1029. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
nautobot/dcim/forms.py CHANGED
@@ -2,11 +2,7 @@ import re
2
2
 
3
3
  from django import forms
4
4
  from django.contrib.auth import get_user_model
5
- from django.contrib.contenttypes.models import ContentType
6
- from django.contrib.postgres.forms.array import SimpleArrayField
7
- from django.core.exceptions import ObjectDoesNotExist, ValidationError
8
5
  from django.db.models import Q
9
- from django.utils.safestring import mark_safe
10
6
  from timezone_field import TimeZoneFormField
11
7
 
12
8
  from nautobot.circuits.models import Circuit, CircuitTermination, Provider
@@ -18,10 +14,6 @@ from nautobot.core.forms import (
18
14
  BulkEditNullBooleanSelect,
19
15
  ColorSelect,
20
16
  CommentField,
21
- CSVChoiceField,
22
- CSVContentTypeField,
23
- CSVModelChoiceField,
24
- CSVMultipleContentTypeField,
25
17
  DynamicModelChoiceField,
26
18
  DynamicModelMultipleChoiceField,
27
19
  ExpandableNameField,
@@ -38,7 +30,6 @@ from nautobot.core.forms import (
38
30
  from nautobot.core.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
39
31
  from nautobot.dcim.form_mixins import (
40
32
  LocatableModelBulkEditFormMixin,
41
- LocatableModelCSVFormMixin,
42
33
  LocatableModelFilterFormMixin,
43
34
  LocatableModelFormMixin,
44
35
  )
@@ -52,17 +43,14 @@ from nautobot.extras.forms import (
52
43
  LocalContextModelForm,
53
44
  LocalContextModelBulkEditForm,
54
45
  RoleModelBulkEditFormMixin,
55
- RoleModelCSVFormMixin,
56
46
  RoleModelFilterFormMixin,
57
- RoleRequiredRoleModelCSVFormMixin,
58
47
  StatusModelBulkEditFormMixin,
59
- StatusModelCSVFormMixin,
60
48
  StatusModelFilterFormMixin,
61
49
  TagsBulkEditFormMixin,
62
50
  )
63
51
  from nautobot.extras.models import SecretsGroup, Status
64
52
  from nautobot.ipam.constants import BGP_ASN_MAX, BGP_ASN_MIN
65
- from nautobot.ipam.models import IPAddress, IPAddressToInterface, VLAN
53
+ from nautobot.ipam.models import IPAddress, IPAddressToInterface, VLAN, VRF
66
54
  from nautobot.tenancy.forms import TenancyFilterForm, TenancyForm
67
55
  from nautobot.tenancy.models import Tenant, TenantGroup
68
56
  from nautobot.virtualization.models import Cluster, ClusterGroup
@@ -87,10 +75,8 @@ from .choices import (
87
75
  SubdeviceRoleChoices,
88
76
  )
89
77
  from .constants import (
90
- CABLE_TERMINATION_MODELS,
91
78
  INTERFACE_MTU_MAX,
92
79
  INTERFACE_MTU_MIN,
93
- NONCONNECTABLE_IFACE_TYPES,
94
80
  REARPORT_POSITIONS_MAX,
95
81
  REARPORT_POSITIONS_MIN,
96
82
  )
@@ -258,29 +244,6 @@ class LocationTypeForm(NautobotModelForm):
258
244
  fields = ("parent", "name", "slug", "description", "nestable", "content_types")
259
245
 
260
246
 
261
- class LocationTypeCSVForm(CustomFieldModelCSVForm):
262
- parent = CSVModelChoiceField(
263
- queryset=LocationType.objects.all(),
264
- required=False,
265
- to_field_name="name",
266
- help_text="Name of parent location type",
267
- )
268
- content_types = CSVMultipleContentTypeField(
269
- feature="locations",
270
- required=False,
271
- choices_as_strings=True,
272
- help_text=mark_safe(
273
- "The object types to which this status applies. Multiple values "
274
- "must be comma-separated and wrapped in double quotes. (e.g. "
275
- '<code>"dcim.device,dcim.rack"</code>)'
276
- ),
277
- )
278
-
279
- class Meta:
280
- model = LocationType
281
- fields = LocationType.csv_headers
282
-
283
-
284
247
  class LocationTypeFilterForm(NautobotFilterForm):
285
248
  model = LocationType
286
249
  q = forms.CharField(required=False, label="Search")
@@ -378,35 +341,6 @@ class LocationBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin,
378
341
  ]
379
342
 
380
343
 
381
- class LocationCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
382
- location_type = CSVModelChoiceField(
383
- queryset=LocationType.objects.all(),
384
- to_field_name="name",
385
- help_text="Location type",
386
- )
387
- parent = CSVModelChoiceField(
388
- queryset=Location.objects.all(),
389
- required=False,
390
- to_field_name="name",
391
- help_text="Parent location",
392
- )
393
- tenant = CSVModelChoiceField(
394
- queryset=Tenant.objects.all(),
395
- required=False,
396
- to_field_name="name",
397
- help_text="Assigned tenant",
398
- )
399
-
400
- class Meta:
401
- model = Location
402
- fields = Location.csv_headers
403
- help_texts = {
404
- "time_zone": mark_safe(
405
- 'Time zone (<a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">available options</a>)'
406
- )
407
- }
408
-
409
-
410
344
  class LocationFilterForm(NautobotFilterForm, StatusModelFilterFormMixin, TenancyFilterForm):
411
345
  model = Location
412
346
  field_order = ["q", "location_type", "parent", "subtree", "status", "tenant_group", "tenant", "tag"]
@@ -444,22 +378,6 @@ class RackGroupForm(LocatableModelFormMixin, NautobotModelForm):
444
378
  )
445
379
 
446
380
 
447
- class RackGroupCSVForm(LocatableModelCSVFormMixin, CustomFieldModelCSVForm):
448
- parent = CSVModelChoiceField(
449
- queryset=RackGroup.objects.all(),
450
- required=False,
451
- to_field_name="name",
452
- help_text="Parent rack group",
453
- error_messages={
454
- "invalid_choice": "Rack group not found.",
455
- },
456
- )
457
-
458
- class Meta:
459
- model = RackGroup
460
- fields = RackGroup.csv_headers
461
-
462
-
463
381
  class RackGroupFilterForm(NautobotFilterForm, LocatableModelFilterFormMixin):
464
382
  model = RackGroup
465
383
  parent = DynamicModelMultipleChoiceField(
@@ -519,36 +437,6 @@ class RackForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm):
519
437
  }
520
438
 
521
439
 
522
- class RackCSVForm(LocatableModelCSVFormMixin, StatusModelCSVFormMixin, RoleModelCSVFormMixin, CustomFieldModelCSVForm):
523
- rack_group = CSVModelChoiceField(queryset=RackGroup.objects.all(), required=False, to_field_name="name")
524
- tenant = CSVModelChoiceField(
525
- queryset=Tenant.objects.all(),
526
- required=False,
527
- to_field_name="name",
528
- help_text="Name of assigned tenant",
529
- )
530
- type = CSVChoiceField(choices=RackTypeChoices, required=False, help_text="Rack type")
531
- width = forms.ChoiceField(choices=RackWidthChoices, help_text="Rail-to-rail width (in inches)")
532
- outer_unit = CSVChoiceField(
533
- choices=RackDimensionUnitChoices,
534
- required=False,
535
- help_text="Unit for outer dimensions",
536
- )
537
-
538
- class Meta:
539
- model = Rack
540
- fields = Rack.csv_headers
541
-
542
- def __init__(self, data=None, *args, **kwargs):
543
- super().__init__(data, *args, **kwargs)
544
-
545
- if data:
546
-
547
- # Limit rack_group queryset by assigned location
548
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
549
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
550
-
551
-
552
440
  class RackBulkEditForm(
553
441
  TagsBulkEditFormMixin,
554
442
  LocatableModelBulkEditFormMixin,
@@ -695,48 +583,6 @@ class RackReservationForm(NautobotModelForm, TenancyForm):
695
583
  ]
696
584
 
697
585
 
698
- class RackReservationCSVForm(CustomFieldModelCSVForm):
699
- location = CSVModelChoiceField(queryset=Location.objects.all(), to_field_name="name", help_text="Parent location")
700
- rack_group = CSVModelChoiceField(
701
- queryset=RackGroup.objects.all(),
702
- to_field_name="name",
703
- required=False,
704
- help_text="Rack's group (if any)",
705
- )
706
- rack = CSVModelChoiceField(queryset=Rack.objects.all(), to_field_name="name", help_text="Rack")
707
- units = SimpleArrayField(
708
- base_field=forms.IntegerField(),
709
- required=True,
710
- help_text="Comma-separated list of individual unit numbers",
711
- )
712
- tenant = CSVModelChoiceField(
713
- queryset=Tenant.objects.all(),
714
- required=False,
715
- to_field_name="name",
716
- help_text="Assigned tenant",
717
- )
718
-
719
- class Meta:
720
- model = RackReservation
721
- fields = ("location", "rack_group", "rack", "units", "tenant", "description")
722
-
723
- def __init__(self, data=None, *args, **kwargs):
724
- super().__init__(data, *args, **kwargs)
725
-
726
- if data:
727
-
728
- # Limit rack_group queryset by assigned location
729
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
730
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
731
-
732
- # Limit rack queryset by assigned location and group
733
- params = {
734
- f"location__{self.fields['location'].to_field_name}": data.get("location"),
735
- f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
736
- }
737
- self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
738
-
739
-
740
586
  class RackReservationBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
741
587
  pk = forms.ModelMultipleChoiceField(queryset=RackReservation.objects.all(), widget=forms.MultipleHiddenInput())
742
588
  user = forms.ModelChoiceField(
@@ -785,23 +631,14 @@ class RackReservationFilterForm(NautobotFilterForm, TenancyFilterForm):
785
631
 
786
632
 
787
633
  class ManufacturerForm(NautobotModelForm):
788
- slug = SlugField()
789
-
790
634
  class Meta:
791
635
  model = Manufacturer
792
636
  fields = [
793
637
  "name",
794
- "slug",
795
638
  "description",
796
639
  ]
797
640
 
798
641
 
799
- class ManufacturerCSVForm(CustomFieldModelCSVForm):
800
- class Meta:
801
- model = Manufacturer
802
- fields = Manufacturer.csv_headers
803
-
804
-
805
642
  #
806
643
  # Device types
807
644
  #
@@ -840,6 +677,14 @@ class DeviceTypeForm(NautobotModelForm):
840
677
 
841
678
 
842
679
  class DeviceTypeImportForm(BootstrapMixin, forms.ModelForm):
680
+ """
681
+ Form for JSON/YAML import of DeviceType objects.
682
+
683
+ TODO: at some point we'll want to add general-purpose YAML serialization/deserialization,
684
+ similar to what we've done for CSV in 2.0, but for the moment we're leaving this as-is so that we can remain
685
+ at least nominally compatible with the netbox-community/devicetype-library repo.
686
+ """
687
+
843
688
  manufacturer = forms.ModelChoiceField(queryset=Manufacturer.objects.all(), to_field_name="name")
844
689
 
845
690
  class Meta:
@@ -1088,7 +933,6 @@ class PowerOutletTemplateForm(NautobotModelForm):
1088
933
  }
1089
934
 
1090
935
  def __init__(self, *args, **kwargs):
1091
-
1092
936
  super().__init__(*args, **kwargs)
1093
937
 
1094
938
  # Limit power_port_template choices to current DeviceType
@@ -1226,7 +1070,6 @@ class FrontPortTemplateForm(NautobotModelForm):
1226
1070
  }
1227
1071
 
1228
1072
  def __init__(self, *args, **kwargs):
1229
-
1230
1073
  super().__init__(*args, **kwargs)
1231
1074
 
1232
1075
  # Limit rear_port_template choices to current DeviceType
@@ -1295,7 +1138,6 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm):
1295
1138
  )
1296
1139
 
1297
1140
  def get_iterative_data(self, iteration):
1298
-
1299
1141
  # Assign rear port and position from selected set
1300
1142
  rear_port_template, position = self.cleaned_data["rear_port_template_set"][iteration].split(":")
1301
1143
 
@@ -1411,8 +1253,15 @@ class DeviceBayTemplateBulkEditForm(NautobotBulkEditForm):
1411
1253
 
1412
1254
 
1413
1255
  class ComponentTemplateImportForm(BootstrapMixin, CustomFieldModelCSVForm):
1414
- def __init__(self, device_type, data=None, *args, **kwargs):
1256
+ """
1257
+ Base form class for JSON/YAML import of device component templates as a part of the DeviceType import form/view.
1258
+
1259
+ TODO: at some point we'll want to switch to general-purpose YAML import support, similar to what we've done for
1260
+ CSV in 2.0, but for now we're keeping this as-is for nominal compatibility with the
1261
+ netbox-community/devicetype-library repository.
1262
+ """
1415
1263
 
1264
+ def __init__(self, device_type, data=None, *args, **kwargs):
1416
1265
  # Must pass the parent DeviceType on form initialization
1417
1266
  data.update(
1418
1267
  {
@@ -1423,7 +1272,6 @@ class ComponentTemplateImportForm(BootstrapMixin, CustomFieldModelCSVForm):
1423
1272
  super().__init__(data, *args, **kwargs)
1424
1273
 
1425
1274
  def clean_device_type(self):
1426
-
1427
1275
  data = self.cleaned_data["device_type"]
1428
1276
 
1429
1277
  # Limit fields referencing other components to the parent DeviceType
@@ -1568,13 +1416,11 @@ class DeviceBayTemplateImportForm(ComponentTemplateImportForm):
1568
1416
 
1569
1417
  class PlatformForm(NautobotModelForm):
1570
1418
  manufacturer = DynamicModelChoiceField(queryset=Manufacturer.objects.all(), required=False)
1571
- slug = SlugField(max_length=64)
1572
1419
 
1573
1420
  class Meta:
1574
1421
  model = Platform
1575
1422
  fields = [
1576
1423
  "name",
1577
- "slug",
1578
1424
  "manufacturer",
1579
1425
  "napalm_driver",
1580
1426
  "napalm_args",
@@ -1585,19 +1431,6 @@ class PlatformForm(NautobotModelForm):
1585
1431
  }
1586
1432
 
1587
1433
 
1588
- class PlatformCSVForm(CustomFieldModelCSVForm):
1589
- manufacturer = CSVModelChoiceField(
1590
- queryset=Manufacturer.objects.all(),
1591
- required=False,
1592
- to_field_name="name",
1593
- help_text="Limit platform assignments to this manufacturer",
1594
- )
1595
-
1596
- class Meta:
1597
- model = Platform
1598
- fields = Platform.csv_headers
1599
-
1600
-
1601
1434
  #
1602
1435
  # Devices
1603
1436
  #
@@ -1656,6 +1489,11 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
1656
1489
  required=False,
1657
1490
  query_params={"group_id": "$cluster_group"},
1658
1491
  )
1492
+ vrfs = DynamicModelMultipleChoiceField(
1493
+ queryset=VRF.objects.all(),
1494
+ required=False,
1495
+ label="VRFs",
1496
+ )
1659
1497
  comments = CommentField()
1660
1498
 
1661
1499
  class Meta:
@@ -1681,6 +1519,7 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
1681
1519
  "cluster",
1682
1520
  "tenant_group",
1683
1521
  "tenant",
1522
+ "vrfs",
1684
1523
  "comments",
1685
1524
  "tags",
1686
1525
  "local_config_context_data",
@@ -1702,9 +1541,8 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
1702
1541
  super().__init__(*args, **kwargs)
1703
1542
 
1704
1543
  if self.instance.present_in_database:
1705
-
1706
1544
  # Compile list of choices for primary IPv4 and IPv6 addresses
1707
- for family in [4, 6]:
1545
+ for ip_version in [4, 6]:
1708
1546
  ip_choices = [(None, "---------")]
1709
1547
 
1710
1548
  # Gather PKs of all interfaces belonging to this Device or a peer VirtualChassis member
@@ -1721,7 +1559,7 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
1721
1559
  f"{assignment.ip_address.address} ({assignment.interface})",
1722
1560
  )
1723
1561
  for assignment in interface_ip_assignments
1724
- if assignment.ip_address.family == family
1562
+ if assignment.ip_address.ip_version == ip_version
1725
1563
  ]
1726
1564
  ip_choices.append(("Interface IPs", ip_list))
1727
1565
 
@@ -1734,11 +1572,11 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
1734
1572
  [
1735
1573
  (ip.id, f"{ip.address} (NAT)")
1736
1574
  for ip in ip_assignment.ip_address.nat_outside_list.all()
1737
- if ip.family == family
1575
+ if ip.ip_version == ip_version
1738
1576
  ]
1739
1577
  )
1740
1578
  ip_choices.append(("NAT IPs", nat_ips))
1741
- self.fields[f"primary_ip{family}"].choices = ip_choices
1579
+ self.fields[f"primary_ip{ip_version}"].choices = ip_choices
1742
1580
 
1743
1581
  # If editing an existing device, exclude it from the list of occupied rack units. This ensures that a device
1744
1582
  # can be flipped from one face to another.
@@ -1755,8 +1593,9 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
1755
1593
  self.initial["location"] = self.instance.parent_bay.device.location_id
1756
1594
  self.initial["rack"] = self.instance.parent_bay.device.rack_id
1757
1595
 
1758
- else:
1596
+ self.initial["vrfs"] = self.instance.vrfs.values_list("id", flat=True)
1759
1597
 
1598
+ else:
1760
1599
  # An object that doesn't exist yet can't have any IPs assigned to it
1761
1600
  self.fields["primary_ip4"].choices = []
1762
1601
  self.fields["primary_ip4"].widget.attrs["readonly"] = True
@@ -1768,166 +1607,10 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
1768
1607
  if position:
1769
1608
  self.fields["position"].widget.choices = [(position, f"U{position}")]
1770
1609
 
1771
-
1772
- class BaseDeviceCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
1773
- tenant = CSVModelChoiceField(
1774
- queryset=Tenant.objects.all(),
1775
- required=False,
1776
- to_field_name="name",
1777
- help_text="Assigned tenant",
1778
- )
1779
- manufacturer = CSVModelChoiceField(
1780
- queryset=Manufacturer.objects.all(),
1781
- to_field_name="name",
1782
- help_text="Device type manufacturer",
1783
- )
1784
- device_type = CSVModelChoiceField(
1785
- queryset=DeviceType.objects.all(),
1786
- to_field_name="model",
1787
- help_text="Device type model",
1788
- )
1789
- platform = CSVModelChoiceField(
1790
- queryset=Platform.objects.all(),
1791
- required=False,
1792
- to_field_name="name",
1793
- help_text="Assigned platform",
1794
- )
1795
- cluster = CSVModelChoiceField(
1796
- queryset=Cluster.objects.all(),
1797
- to_field_name="name",
1798
- required=False,
1799
- help_text="Virtualization cluster",
1800
- )
1801
- secrets_group = CSVModelChoiceField(
1802
- queryset=SecretsGroup.objects.all(),
1803
- required=False,
1804
- to_field_name="name",
1805
- help_text="Secrets group",
1806
- )
1807
-
1808
- class Meta:
1809
- fields = []
1810
- model = Device
1811
-
1812
- def __init__(self, data=None, *args, **kwargs):
1813
- super().__init__(data, *args, **kwargs)
1814
-
1815
- if data:
1816
-
1817
- # Limit device type queryset by manufacturer
1818
- params = {f"manufacturer__{self.fields['manufacturer'].to_field_name}": data.get("manufacturer")}
1819
- self.fields["device_type"].queryset = self.fields["device_type"].queryset.filter(**params)
1820
-
1821
-
1822
- class DeviceCSVForm(LocatableModelCSVFormMixin, BaseDeviceCSVForm, RoleRequiredRoleModelCSVFormMixin):
1823
- rack_group = CSVModelChoiceField(
1824
- queryset=RackGroup.objects.all(),
1825
- to_field_name="name",
1826
- required=False,
1827
- help_text="Rack's group (if any)",
1828
- )
1829
- rack = CSVModelChoiceField(
1830
- queryset=Rack.objects.all(),
1831
- to_field_name="name",
1832
- required=False,
1833
- help_text="Assigned rack",
1834
- )
1835
- face = CSVChoiceField(choices=DeviceFaceChoices, required=False, help_text="Mounted rack face")
1836
- device_redundancy_group = CSVModelChoiceField(
1837
- queryset=DeviceRedundancyGroup.objects.all(),
1838
- to_field_name="slug",
1839
- required=False,
1840
- help_text="Associated device redundancy group (slug)",
1841
- )
1842
-
1843
- class Meta(BaseDeviceCSVForm.Meta):
1844
- fields = [
1845
- "name",
1846
- "role",
1847
- "tenant",
1848
- "manufacturer",
1849
- "device_type",
1850
- "platform",
1851
- "serial",
1852
- "asset_tag",
1853
- "status",
1854
- "location",
1855
- "rack_group",
1856
- "rack",
1857
- "position",
1858
- "face",
1859
- "device_redundancy_group",
1860
- "device_redundancy_group_priority",
1861
- "cluster",
1862
- "comments",
1863
- ]
1864
-
1865
- def __init__(self, data=None, *args, **kwargs):
1866
- super().__init__(data, *args, **kwargs)
1867
-
1868
- if data:
1869
-
1870
- # Limit rack_group queryset by assigned location
1871
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
1872
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
1873
-
1874
- # Limit rack queryset by assigned location and group
1875
- params = {
1876
- f"location__{self.fields['location'].to_field_name}": data.get("location"),
1877
- f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
1878
- }
1879
- self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
1880
-
1881
- # 2.0 TODO: limit location queryset by assigned location
1882
-
1883
-
1884
- class ChildDeviceCSVForm(BaseDeviceCSVForm):
1885
- parent = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Parent device")
1886
- device_bay = CSVModelChoiceField(
1887
- queryset=DeviceBay.objects.all(),
1888
- to_field_name="name",
1889
- help_text="Device bay in which this device is installed",
1890
- )
1891
-
1892
- class Meta(BaseDeviceCSVForm.Meta):
1893
- fields = [
1894
- "name",
1895
- "role",
1896
- "tenant",
1897
- "manufacturer",
1898
- "device_type",
1899
- "platform",
1900
- "serial",
1901
- "asset_tag",
1902
- "status",
1903
- "parent",
1904
- "device_bay",
1905
- "cluster",
1906
- "comments",
1907
- ]
1908
-
1909
- def __init__(self, data=None, *args, **kwargs):
1910
- super().__init__(data, *args, **kwargs)
1911
-
1912
- if data:
1913
-
1914
- # Limit device bay queryset by parent device
1915
- params = {f"device__{self.fields['parent'].to_field_name}": data.get("parent")}
1916
- self.fields["device_bay"].queryset = self.fields["device_bay"].queryset.filter(**params)
1917
-
1918
- def clean(self):
1919
- super().clean()
1920
-
1921
- # Set parent_bay reverse relationship
1922
- device_bay = self.cleaned_data.get("device_bay")
1923
- if device_bay:
1924
- self.instance.parent_bay = device_bay
1925
-
1926
- # Inherit location and rack from parent device
1927
- parent = self.cleaned_data.get("parent")
1928
- if parent:
1929
- self.instance.location = parent.location
1930
- self.instance.rack = parent.rack
1610
+ def save(self, *args, **kwargs):
1611
+ instance = super().save(*args, **kwargs)
1612
+ instance.vrfs.set(self.cleaned_data["vrfs"])
1613
+ return instance
1931
1614
 
1932
1615
 
1933
1616
  class DeviceBulkEditForm(
@@ -2025,7 +1708,7 @@ class DeviceFilterForm(
2025
1708
  )
2026
1709
  manufacturer = DynamicModelMultipleChoiceField(
2027
1710
  queryset=Manufacturer.objects.all(),
2028
- to_field_name="slug",
1711
+ to_field_name="name",
2029
1712
  required=False,
2030
1713
  label="Manufacturer",
2031
1714
  )
@@ -2037,14 +1720,14 @@ class DeviceFilterForm(
2037
1720
  )
2038
1721
  platform = DynamicModelMultipleChoiceField(
2039
1722
  queryset=Platform.objects.all(),
2040
- to_field_name="slug",
1723
+ to_field_name="name",
2041
1724
  required=False,
2042
1725
  null_option="None",
2043
1726
  )
2044
1727
  mac_address = forms.CharField(required=False, label="MAC address")
2045
1728
  device_redundancy_group = DynamicModelMultipleChoiceField(
2046
1729
  queryset=DeviceRedundancyGroup.objects.all(),
2047
- to_field_name="slug",
1730
+ to_field_name="name",
2048
1731
  required=False,
2049
1732
  null_option="None",
2050
1733
  )
@@ -2177,15 +1860,6 @@ class ConsolePortBulkEditForm(
2177
1860
  nullable_fields = ["label", "description"]
2178
1861
 
2179
1862
 
2180
- class ConsolePortCSVForm(CustomFieldModelCSVForm):
2181
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2182
- type = CSVChoiceField(choices=ConsolePortTypeChoices, required=False, help_text="Port type")
2183
-
2184
- class Meta:
2185
- model = ConsolePort
2186
- fields = ConsolePort.csv_headers
2187
-
2188
-
2189
1863
  #
2190
1864
  # Console server ports
2191
1865
  #
@@ -2244,15 +1918,6 @@ class ConsoleServerPortBulkEditForm(
2244
1918
  nullable_fields = ["label", "description"]
2245
1919
 
2246
1920
 
2247
- class ConsoleServerPortCSVForm(CustomFieldModelCSVForm):
2248
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2249
- type = CSVChoiceField(choices=ConsolePortTypeChoices, required=False, help_text="Port type")
2250
-
2251
- class Meta:
2252
- model = ConsoleServerPort
2253
- fields = ConsoleServerPort.csv_headers
2254
-
2255
-
2256
1921
  #
2257
1922
  # Power ports
2258
1923
  #
@@ -2328,15 +1993,6 @@ class PowerPortBulkEditForm(
2328
1993
  nullable_fields = ["label", "description"]
2329
1994
 
2330
1995
 
2331
- class PowerPortCSVForm(CustomFieldModelCSVForm):
2332
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2333
- type = CSVChoiceField(choices=PowerPortTypeChoices, required=False, help_text="Port type")
2334
-
2335
- class Meta:
2336
- model = PowerPort
2337
- fields = PowerPort.csv_headers
2338
-
2339
-
2340
1996
  #
2341
1997
  # Power outlets
2342
1998
  #
@@ -2441,46 +2097,6 @@ class PowerOutletBulkEditForm(
2441
2097
  self.fields["power_port"].widget.attrs["disabled"] = True
2442
2098
 
2443
2099
 
2444
- class PowerOutletCSVForm(CustomFieldModelCSVForm):
2445
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2446
- type = CSVChoiceField(choices=PowerOutletTypeChoices, required=False, help_text="Outlet type")
2447
- power_port = CSVModelChoiceField(
2448
- queryset=PowerPort.objects.all(),
2449
- required=False,
2450
- to_field_name="name",
2451
- help_text="Local power port which feeds this outlet",
2452
- )
2453
- feed_leg = CSVChoiceField(
2454
- choices=PowerOutletFeedLegChoices,
2455
- required=False,
2456
- help_text="Electrical phase (for three-phase circuits)",
2457
- )
2458
-
2459
- class Meta:
2460
- model = PowerOutlet
2461
- fields = PowerOutlet.csv_headers
2462
-
2463
- def __init__(self, *args, **kwargs):
2464
- super().__init__(*args, **kwargs)
2465
-
2466
- # Limit PowerPort choices to those belonging to this device (or VC master)
2467
- if self.is_bound:
2468
- try:
2469
- device = self.fields["device"].to_python(self.data["device"])
2470
- except forms.ValidationError:
2471
- device = None
2472
- else:
2473
- try:
2474
- device = self.instance.device
2475
- except Device.DoesNotExist:
2476
- device = None
2477
-
2478
- if device:
2479
- self.fields["power_port"].queryset = PowerPort.objects.filter(device__in=[device, device.get_vc_master()])
2480
- else:
2481
- self.fields["power_port"].queryset = PowerPort.objects.none()
2482
-
2483
-
2484
2100
  #
2485
2101
  # Interfaces
2486
2102
  #
@@ -2524,7 +2140,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
2524
2140
  queryset=VLAN.objects.all(),
2525
2141
  required=False,
2526
2142
  label="Untagged VLAN",
2527
- brief_mode=False,
2528
2143
  query_params={
2529
2144
  "location": "null",
2530
2145
  },
@@ -2533,7 +2148,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
2533
2148
  queryset=VLAN.objects.all(),
2534
2149
  required=False,
2535
2150
  label="Tagged VLANs",
2536
- brief_mode=False,
2537
2151
  query_params={
2538
2152
  "location": "null",
2539
2153
  },
@@ -2542,7 +2156,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
2542
2156
  queryset=IPAddress.objects.all(),
2543
2157
  required=False,
2544
2158
  label="IP Addresses",
2545
- brief_mode=False,
2546
2159
  )
2547
2160
 
2548
2161
  class Meta:
@@ -2559,6 +2172,7 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
2559
2172
  "mac_address",
2560
2173
  "ip_addresses",
2561
2174
  "mtu",
2175
+ "vrf",
2562
2176
  "mgmt_only",
2563
2177
  "description",
2564
2178
  "mode",
@@ -2571,9 +2185,11 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
2571
2185
  "device": forms.HiddenInput(),
2572
2186
  "type": StaticSelect2(),
2573
2187
  "mode": StaticSelect2(),
2188
+ "vrf": StaticSelect2(),
2574
2189
  }
2575
2190
  labels = {
2576
2191
  "mode": "802.1Q Mode",
2192
+ "vrf": "VRF",
2577
2193
  }
2578
2194
  help_texts = {
2579
2195
  "mode": INTERFACE_MODE_HELP_TEXT,
@@ -2641,6 +2257,14 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
2641
2257
  max_value=INTERFACE_MTU_MAX,
2642
2258
  label="MTU",
2643
2259
  )
2260
+ vrf = DynamicModelChoiceField(
2261
+ queryset=VRF.objects.all(),
2262
+ label="VRF",
2263
+ required=False,
2264
+ query_params={
2265
+ "device": "$device",
2266
+ },
2267
+ )
2644
2268
  mac_address = forms.CharField(required=False, label="MAC Address")
2645
2269
  mgmt_only = forms.BooleanField(
2646
2270
  required=False,
@@ -2655,7 +2279,6 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
2655
2279
  untagged_vlan = DynamicModelChoiceField(
2656
2280
  queryset=VLAN.objects.all(),
2657
2281
  required=False,
2658
- brief_mode=False,
2659
2282
  query_params={
2660
2283
  "available_on_device": "$device",
2661
2284
  },
@@ -2663,7 +2286,6 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
2663
2286
  tagged_vlans = DynamicModelMultipleChoiceField(
2664
2287
  queryset=VLAN.objects.all(),
2665
2288
  required=False,
2666
- brief_mode=False,
2667
2289
  query_params={"available_on_device": "$device"},
2668
2290
  )
2669
2291
  field_order = (
@@ -2677,6 +2299,7 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
2677
2299
  "bridge",
2678
2300
  "lag",
2679
2301
  "mtu",
2302
+ "vrf",
2680
2303
  "mac_address",
2681
2304
  "description",
2682
2305
  "mgmt_only",
@@ -2688,7 +2311,7 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
2688
2311
 
2689
2312
 
2690
2313
  class InterfaceBulkCreateForm(
2691
- form_from_model(Interface, ["enabled", "mtu", "mgmt_only", "mode", "tags"]),
2314
+ form_from_model(Interface, ["enabled", "mtu", "vrf", "mgmt_only", "mode", "tags"]),
2692
2315
  DeviceBulkAddComponentForm,
2693
2316
  ):
2694
2317
  type = forms.ChoiceField(
@@ -2708,6 +2331,7 @@ class InterfaceBulkCreateForm(
2708
2331
  "type",
2709
2332
  "enabled",
2710
2333
  "mtu",
2334
+ "vrf",
2711
2335
  "mgmt_only",
2712
2336
  "description",
2713
2337
  "mode",
@@ -2753,7 +2377,6 @@ class InterfaceBulkEditForm(
2753
2377
  untagged_vlan = DynamicModelChoiceField(
2754
2378
  queryset=VLAN.objects.all(),
2755
2379
  required=False,
2756
- brief_mode=False,
2757
2380
  query_params={
2758
2381
  "location": "null",
2759
2382
  },
@@ -2761,7 +2384,6 @@ class InterfaceBulkEditForm(
2761
2384
  tagged_vlans = DynamicModelMultipleChoiceField(
2762
2385
  queryset=VLAN.objects.all(),
2763
2386
  required=False,
2764
- brief_mode=False,
2765
2387
  query_params={
2766
2388
  "location": "null",
2767
2389
  },
@@ -2834,67 +2456,6 @@ class InterfaceBulkEditForm(
2834
2456
  self.cleaned_data["tagged_vlans"] = []
2835
2457
 
2836
2458
 
2837
- class InterfaceCSVForm(CustomFieldModelCSVForm, StatusModelCSVFormMixin):
2838
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2839
- parent_interface = CSVModelChoiceField(
2840
- queryset=Interface.objects.all(), required=False, to_field_name="name", help_text="Parent interface"
2841
- )
2842
- bridge = CSVModelChoiceField(
2843
- queryset=Interface.objects.all(), required=False, to_field_name="name", help_text="Bridge interface"
2844
- )
2845
- lag = CSVModelChoiceField(
2846
- queryset=Interface.objects.all(),
2847
- required=False,
2848
- to_field_name="name",
2849
- help_text="Parent LAG interface",
2850
- )
2851
- type = CSVChoiceField(choices=InterfaceTypeChoices, help_text="Physical medium")
2852
- mode = CSVChoiceField(
2853
- choices=InterfaceModeChoices,
2854
- required=False,
2855
- help_text="IEEE 802.1Q operational mode (for L2 interfaces)",
2856
- )
2857
-
2858
- def __init__(self, data=None, *args, **kwargs):
2859
- super().__init__(data, *args, **kwargs)
2860
-
2861
- if data:
2862
- # Limit choices for parent, bridge, and LAG interfaces to the assigned device (or VC)
2863
- device_name = data.get("device")
2864
- if device_name is not None:
2865
- device = Device.objects.filter(name=device_name).first()
2866
-
2867
- filter_by = Q(device=device)
2868
-
2869
- if device and device.virtual_chassis:
2870
- filter_by |= Q(device__virtual_chassis=device.virtual_chassis)
2871
-
2872
- self.fields["parent_interface"].queryset = (
2873
- self.fields["parent_interface"]
2874
- .queryset.filter(Q(filter_by))
2875
- .exclude(type__in=NONCONNECTABLE_IFACE_TYPES)
2876
- )
2877
- self.fields["bridge"].queryset = self.fields["bridge"].queryset.filter(filter_by)
2878
-
2879
- filter_by &= Q(type=InterfaceTypeChoices.TYPE_LAG)
2880
- self.fields["lag"].queryset = self.fields["lag"].queryset.filter(filter_by)
2881
- else:
2882
- self.fields["parent_interface"].queryset = self.fields["parent_interface"].queryset.none()
2883
- self.fields["bridge"].queryset = self.fields["bridge"].queryset.none()
2884
- self.fields["lag"].queryset = self.fields["lag"].queryset.none()
2885
-
2886
- class Meta:
2887
- model = Interface
2888
- fields = Interface.csv_headers
2889
-
2890
- def clean_enabled(self):
2891
- # Make sure enabled is True when it's not included in the uploaded data
2892
- if "enabled" not in self.data:
2893
- return True
2894
- else:
2895
- return self.cleaned_data["enabled"]
2896
-
2897
-
2898
2459
  #
2899
2460
  # Front pass-through ports
2900
2461
  #
@@ -2996,7 +2557,6 @@ class FrontPortCreateForm(ComponentCreateForm):
2996
2557
  )
2997
2558
 
2998
2559
  def get_iterative_data(self, iteration):
2999
-
3000
2560
  # Assign rear port and position from selected set
3001
2561
  rear_port, position = self.cleaned_data["rear_port_set"][iteration].split(":")
3002
2562
 
@@ -3024,43 +2584,6 @@ class FrontPortBulkEditForm(
3024
2584
  nullable_fields = ["label", "description"]
3025
2585
 
3026
2586
 
3027
- class FrontPortCSVForm(CustomFieldModelCSVForm):
3028
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
3029
- rear_port = CSVModelChoiceField(
3030
- queryset=RearPort.objects.all(),
3031
- to_field_name="name",
3032
- help_text="Corresponding rear port",
3033
- )
3034
- type = CSVChoiceField(choices=PortTypeChoices, help_text="Physical medium classification")
3035
-
3036
- class Meta:
3037
- model = FrontPort
3038
- fields = FrontPort.csv_headers
3039
- help_texts = {
3040
- "rear_port_position": "Mapped position on corresponding rear port",
3041
- }
3042
-
3043
- def __init__(self, *args, **kwargs):
3044
- super().__init__(*args, **kwargs)
3045
-
3046
- # Limit RearPort choices to those belonging to this device (or VC master)
3047
- if self.is_bound:
3048
- try:
3049
- device = self.fields["device"].to_python(self.data["device"])
3050
- except forms.ValidationError:
3051
- device = None
3052
- else:
3053
- try:
3054
- device = self.instance.device
3055
- except Device.DoesNotExist:
3056
- device = None
3057
-
3058
- if device:
3059
- self.fields["rear_port"].queryset = RearPort.objects.filter(device__in=[device, device.get_vc_master()])
3060
- else:
3061
- self.fields["rear_port"].queryset = RearPort.objects.none()
3062
-
3063
-
3064
2587
  #
3065
2588
  # Rear pass-through ports
3066
2589
  #
@@ -3134,19 +2657,6 @@ class RearPortBulkEditForm(
3134
2657
  nullable_fields = ["label", "description"]
3135
2658
 
3136
2659
 
3137
- class RearPortCSVForm(CustomFieldModelCSVForm):
3138
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
3139
- type = CSVChoiceField(
3140
- help_text="Physical medium classification",
3141
- choices=PortTypeChoices,
3142
- )
3143
-
3144
- class Meta:
3145
- model = RearPort
3146
- fields = RearPort.csv_headers
3147
- help_texts = {"positions": "Number of front ports which may be mapped"}
3148
-
3149
-
3150
2660
  #
3151
2661
  # Device bays
3152
2662
  #
@@ -3185,7 +2695,6 @@ class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
3185
2695
  )
3186
2696
 
3187
2697
  def __init__(self, device_bay, *args, **kwargs):
3188
-
3189
2698
  super().__init__(*args, **kwargs)
3190
2699
 
3191
2700
  self.fields["installed_device"].queryset = Device.objects.filter(
@@ -3212,49 +2721,6 @@ class DeviceBayBulkEditForm(
3212
2721
  nullable_fields = ["label", "description"]
3213
2722
 
3214
2723
 
3215
- class DeviceBayCSVForm(CustomFieldModelCSVForm):
3216
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
3217
- installed_device = CSVModelChoiceField(
3218
- queryset=Device.objects.all(),
3219
- required=False,
3220
- to_field_name="name",
3221
- help_text="Child device installed within this bay",
3222
- error_messages={
3223
- "invalid_choice": "Child device not found.",
3224
- },
3225
- )
3226
-
3227
- class Meta:
3228
- model = DeviceBay
3229
- fields = DeviceBay.csv_headers
3230
-
3231
- def __init__(self, *args, **kwargs):
3232
- super().__init__(*args, **kwargs)
3233
-
3234
- # Limit installed device choices to devices of the correct type and location
3235
- if self.is_bound:
3236
- try:
3237
- device = self.fields["device"].to_python(self.data["device"])
3238
- except forms.ValidationError:
3239
- device = None
3240
- else:
3241
- try:
3242
- device = self.instance.device
3243
- except Device.DoesNotExist:
3244
- device = None
3245
-
3246
- if device:
3247
- self.fields["installed_device"].queryset = Device.objects.filter(
3248
- location=device.location,
3249
- rack=device.rack,
3250
- parent_bay__isnull=True,
3251
- device_type__u_height=0,
3252
- device_type__subdevice_role=SubdeviceRoleChoices.ROLE_CHILD,
3253
- ).exclude(pk=device.pk)
3254
- else:
3255
- self.fields["installed_device"].queryset = Interface.objects.none()
3256
-
3257
-
3258
2724
  #
3259
2725
  # Inventory items
3260
2726
  #
@@ -3315,15 +2781,6 @@ class InventoryItemCreateForm(ComponentCreateForm):
3315
2781
  )
3316
2782
 
3317
2783
 
3318
- class InventoryItemCSVForm(CustomFieldModelCSVForm):
3319
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
3320
- manufacturer = CSVModelChoiceField(queryset=Manufacturer.objects.all(), to_field_name="name", required=False)
3321
-
3322
- class Meta:
3323
- model = InventoryItem
3324
- fields = InventoryItem.csv_headers
3325
-
3326
-
3327
2784
  class InventoryItemBulkCreateForm(
3328
2785
  form_from_model(InventoryItem, ["manufacturer", "part_id", "serial", "asset_tag", "discovered", "tags"]),
3329
2786
  DeviceBulkAddComponentForm,
@@ -3356,7 +2813,7 @@ class InventoryItemBulkEditForm(
3356
2813
  class InventoryItemFilterForm(DeviceComponentFilterForm):
3357
2814
  model = InventoryItem
3358
2815
  manufacturer = DynamicModelMultipleChoiceField(
3359
- queryset=Manufacturer.objects.all(), to_field_name="slug", required=False
2816
+ queryset=Manufacturer.objects.all(), to_field_name="name", required=False
3360
2817
  )
3361
2818
  serial = forms.CharField(required=False)
3362
2819
  asset_tag = forms.CharField(required=False)
@@ -3600,109 +3057,6 @@ class CableForm(NautobotModelForm):
3600
3057
  error_messages = {"length": {"max_value": "Maximum length is 32767 (any unit)"}}
3601
3058
 
3602
3059
 
3603
- class CableCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
3604
- # Termination A
3605
- side_a_device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Side A device")
3606
- side_a_type = CSVContentTypeField(
3607
- queryset=ContentType.objects.all(),
3608
- limit_choices_to=CABLE_TERMINATION_MODELS,
3609
- help_text="Side A type",
3610
- )
3611
- side_a_name = forms.CharField(help_text="Side A component name")
3612
-
3613
- # Termination B
3614
- side_b_device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Side B device")
3615
- side_b_type = CSVContentTypeField(
3616
- queryset=ContentType.objects.all(),
3617
- limit_choices_to=CABLE_TERMINATION_MODELS,
3618
- help_text="Side B type",
3619
- )
3620
- side_b_name = forms.CharField(help_text="Side B component name")
3621
-
3622
- # Cable attributes
3623
- type = CSVChoiceField(
3624
- choices=CableTypeChoices,
3625
- required=False,
3626
- help_text="Physical medium classification",
3627
- )
3628
- length_unit = CSVChoiceField(choices=CableLengthUnitChoices, required=False, help_text="Length unit")
3629
-
3630
- class Meta:
3631
- model = Cable
3632
- fields = [
3633
- "side_a_device",
3634
- "side_a_type",
3635
- "side_a_name",
3636
- "side_b_device",
3637
- "side_b_type",
3638
- "side_b_name",
3639
- "type",
3640
- "status",
3641
- "label",
3642
- "color",
3643
- "length",
3644
- "length_unit",
3645
- ]
3646
- help_texts = {
3647
- "color": mark_safe("RGB color in hexadecimal (e.g. <code>00ff00</code>)"),
3648
- "status": "Connection status",
3649
- }
3650
-
3651
- def _clean_side(self, side):
3652
- """
3653
- Derive a Cable's A/B termination objects.
3654
-
3655
- :param side: 'a' or 'b'
3656
- """
3657
- assert side in "ab", f"Invalid side designation: {side}"
3658
-
3659
- device = self.cleaned_data.get(f"side_{side}_device")
3660
- content_type = self.cleaned_data.get(f"side_{side}_type")
3661
- name = self.cleaned_data.get(f"side_{side}_name")
3662
- if not device or not content_type or not name:
3663
- return None
3664
-
3665
- model = content_type.model_class()
3666
- try:
3667
- termination_object = model.objects.get(device=device, name=name)
3668
- if termination_object.cable is not None:
3669
- raise forms.ValidationError(f"Side {side.upper()}: {device} {termination_object} is already connected")
3670
- except ObjectDoesNotExist:
3671
- raise forms.ValidationError(f"{side.upper()} side termination not found: {device} {name}")
3672
-
3673
- setattr(self.instance, f"termination_{side}", termination_object)
3674
- return termination_object
3675
-
3676
- def clean_side_a_name(self):
3677
- return self._clean_side("a")
3678
-
3679
- def clean_side_b_name(self):
3680
- return self._clean_side("b")
3681
-
3682
- def clean_length_unit(self):
3683
- # Avoid trying to save as NULL
3684
- length_unit = self.cleaned_data.get("length_unit", None)
3685
- return length_unit if length_unit is not None else ""
3686
-
3687
- def add_error(self, field, error):
3688
- # Edge Case: some fields in error are not properties in this instance
3689
- # e.g: termination_a_id not an property in CableCSVForm, This would raise a ValueError Exception
3690
- # Solution: convert those fields to its equivalent in CableCSVForm
3691
- # e.g: termination_a_id > side_a_name
3692
-
3693
- final_error = error
3694
- if hasattr(error, "error_dict"):
3695
- error_dict = error.error_dict
3696
- termination_keys = [key for key in error_dict.keys() if key.startswith("termination")]
3697
- for error_field in termination_keys:
3698
- side_value = error_field.split("_")[1]
3699
- error_msg = error_dict.pop(error_field)
3700
- error_dict[f"side_{side_value}_name"] = error_msg
3701
-
3702
- final_error = ValidationError(error_dict)
3703
- super().add_error(field, final_error)
3704
-
3705
-
3706
3060
  class CableBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, NautobotBulkEditForm):
3707
3061
  pk = forms.ModelMultipleChoiceField(queryset=Cable.objects.all(), widget=forms.MultipleHiddenInput)
3708
3062
  type = forms.ChoiceField(
@@ -3744,7 +3098,7 @@ class CableFilterForm(BootstrapMixin, StatusModelFilterFormMixin, forms.Form):
3744
3098
  model = Cable
3745
3099
  q = forms.CharField(required=False, label="Search")
3746
3100
  location = DynamicModelMultipleChoiceField(queryset=Location.objects.all(), to_field_name="slug", required=False)
3747
- tenant = DynamicModelMultipleChoiceField(queryset=Tenant.objects.all(), to_field_name="slug", required=False)
3101
+ tenant = DynamicModelMultipleChoiceField(queryset=Tenant.objects.all(), to_field_name="name", required=False)
3748
3102
  rack = DynamicModelMultipleChoiceField(
3749
3103
  queryset=Rack.objects.all(),
3750
3104
  required=False,
@@ -3782,6 +3136,7 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
3782
3136
  queryset=Device.objects.all(),
3783
3137
  required=False,
3784
3138
  label="Device",
3139
+ to_field_name="name",
3785
3140
  query_params={"location": "$location"},
3786
3141
  )
3787
3142
 
@@ -3792,6 +3147,7 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
3792
3147
  queryset=Device.objects.all(),
3793
3148
  required=False,
3794
3149
  label="Device",
3150
+ to_field_name="name",
3795
3151
  query_params={"location": "$location"},
3796
3152
  )
3797
3153
 
@@ -3802,6 +3158,7 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
3802
3158
  queryset=Device.objects.all(),
3803
3159
  required=False,
3804
3160
  label="Device",
3161
+ to_field_name="name",
3805
3162
  query_params={"location": "$location"},
3806
3163
  )
3807
3164
 
@@ -3969,32 +3326,19 @@ class VirtualChassisBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
3969
3326
  nullable_fields = ["domain"]
3970
3327
 
3971
3328
 
3972
- class VirtualChassisCSVForm(CustomFieldModelCSVForm):
3973
- master = CSVModelChoiceField(
3974
- queryset=Device.objects.all(),
3975
- to_field_name="name",
3976
- required=False,
3977
- help_text="Master device",
3978
- )
3979
-
3980
- class Meta:
3981
- model = VirtualChassis
3982
- fields = VirtualChassis.csv_headers
3983
-
3984
-
3985
3329
  class VirtualChassisFilterForm(NautobotFilterForm):
3986
3330
  model = VirtualChassis
3987
3331
  q = forms.CharField(required=False, label="Search")
3988
3332
  location = DynamicModelMultipleChoiceField(queryset=Location.objects.all(), to_field_name="slug", required=False)
3989
3333
  tenant_group = DynamicModelMultipleChoiceField(
3990
3334
  queryset=TenantGroup.objects.all(),
3991
- to_field_name="slug",
3335
+ to_field_name="name",
3992
3336
  required=False,
3993
3337
  null_option="None",
3994
3338
  )
3995
3339
  tenant = DynamicModelMultipleChoiceField(
3996
3340
  queryset=Tenant.objects.all(),
3997
- to_field_name="slug",
3341
+ to_field_name="name",
3998
3342
  required=False,
3999
3343
  null_option="None",
4000
3344
  query_params={"tenant_group": "$tenant_group"},
@@ -4024,23 +3368,6 @@ class PowerPanelForm(LocatableModelFormMixin, NautobotModelForm):
4024
3368
  ]
4025
3369
 
4026
3370
 
4027
- class PowerPanelCSVForm(LocatableModelCSVFormMixin, CustomFieldModelCSVForm):
4028
- rack_group = CSVModelChoiceField(queryset=RackGroup.objects.all(), required=False, to_field_name="name")
4029
-
4030
- class Meta:
4031
- model = PowerPanel
4032
- fields = PowerPanel.csv_headers
4033
-
4034
- def __init__(self, data=None, *args, **kwargs):
4035
- super().__init__(data, *args, **kwargs)
4036
-
4037
- if data:
4038
-
4039
- # Limit rack_group queryset by assigned location
4040
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
4041
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
4042
-
4043
-
4044
3371
  class PowerPanelBulkEditForm(
4045
3372
  TagsBulkEditFormMixin,
4046
3373
  LocatableModelBulkEditFormMixin,
@@ -4114,54 +3441,6 @@ class PowerFeedForm(NautobotModelForm):
4114
3441
  }
4115
3442
 
4116
3443
 
4117
- class PowerFeedCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
4118
- location = CSVModelChoiceField(queryset=Location.objects.all(), to_field_name="name", help_text="Assigned location")
4119
- power_panel = CSVModelChoiceField(
4120
- queryset=PowerPanel.objects.all(),
4121
- to_field_name="name",
4122
- help_text="Upstream power panel",
4123
- )
4124
- rack_group = CSVModelChoiceField(
4125
- queryset=RackGroup.objects.all(),
4126
- to_field_name="name",
4127
- required=False,
4128
- help_text="Rack's group (if any)",
4129
- )
4130
- rack = CSVModelChoiceField(
4131
- queryset=Rack.objects.all(),
4132
- to_field_name="name",
4133
- required=False,
4134
- help_text="Rack",
4135
- )
4136
- type = CSVChoiceField(choices=PowerFeedTypeChoices, required=False, help_text="Primary or redundant")
4137
- supply = CSVChoiceField(choices=PowerFeedSupplyChoices, required=False, help_text="Supply type (AC/DC)")
4138
- phase = CSVChoiceField(choices=PowerFeedPhaseChoices, required=False, help_text="Single or three-phase")
4139
-
4140
- class Meta:
4141
- model = PowerFeed
4142
- fields = PowerFeed.csv_headers
4143
-
4144
- def __init__(self, data=None, *args, **kwargs):
4145
- super().__init__(data, *args, **kwargs)
4146
-
4147
- if data:
4148
-
4149
- # Limit power_panel queryset by location
4150
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
4151
- self.fields["power_panel"].queryset = self.fields["power_panel"].queryset.filter(**params)
4152
-
4153
- # Limit rack_group queryset by location
4154
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
4155
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
4156
-
4157
- # Limit rack queryset by location and group
4158
- params = {
4159
- f"location__{self.fields['location'].to_field_name}": data.get("location"),
4160
- f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
4161
- }
4162
- self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
4163
-
4164
-
4165
3444
  class PowerFeedBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, NautobotBulkEditForm):
4166
3445
  pk = forms.ModelMultipleChoiceField(queryset=PowerFeed.objects.all(), widget=forms.MultipleHiddenInput)
4167
3446
  power_panel = DynamicModelChoiceField(queryset=PowerPanel.objects.all(), required=False)
@@ -4237,7 +3516,6 @@ class PowerFeedFilterForm(NautobotFilterForm, StatusModelFilterFormMixin):
4237
3516
  class DeviceRedundancyGroupForm(NautobotModelForm):
4238
3517
  secrets_group = DynamicModelChoiceField(queryset=SecretsGroup.objects.all(), required=False)
4239
3518
  comments = CommentField()
4240
- slug = SlugField()
4241
3519
 
4242
3520
  class Meta:
4243
3521
  model = DeviceRedundancyGroup
@@ -4255,7 +3533,7 @@ class DeviceRedundancyGroupFilterForm(NautobotFilterForm, StatusModelFilterFormM
4255
3533
  widget=StaticSelect2(),
4256
3534
  )
4257
3535
  secrets_group = DynamicModelMultipleChoiceField(
4258
- queryset=SecretsGroup.objects.all(), to_field_name="slug", required=False
3536
+ queryset=SecretsGroup.objects.all(), to_field_name="name", required=False
4259
3537
  )
4260
3538
 
4261
3539
  tag = TagFilterField(model)
@@ -4279,20 +3557,3 @@ class DeviceRedundancyGroupBulkEditForm(
4279
3557
  "failover_strategy",
4280
3558
  "secrets_group",
4281
3559
  ]
4282
-
4283
-
4284
- class DeviceRedundancyGroupCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
4285
- failover_strategy = CSVChoiceField(
4286
- choices=DeviceRedundancyGroupFailoverStrategyChoices, required=False, help_text="Failover Strategy"
4287
- )
4288
-
4289
- secrets_group = CSVModelChoiceField(
4290
- queryset=SecretsGroup.objects.all(),
4291
- required=False,
4292
- to_field_name="name",
4293
- help_text="Secrets group",
4294
- )
4295
-
4296
- class Meta:
4297
- model = DeviceRedundancyGroup
4298
- fields = DeviceRedundancyGroup.csv_headers