nautobot 2.0.0a3__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 (780) hide show
  1. nautobot/apps/api.py +6 -8
  2. nautobot/apps/forms.py +0 -2
  3. nautobot/apps/ui.py +0 -8
  4. nautobot/circuits/api/serializers.py +9 -117
  5. nautobot/circuits/api/urls.py +1 -1
  6. nautobot/circuits/api/views.py +0 -1
  7. nautobot/circuits/forms.py +0 -65
  8. nautobot/circuits/migrations/0014_related_name_changes.py +1 -1
  9. nautobot/circuits/migrations/0016_tagsfield.py +34 -0
  10. nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
  11. nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
  12. nautobot/circuits/models.py +3 -87
  13. nautobot/circuits/navigation.py +14 -69
  14. nautobot/circuits/signals.py +0 -2
  15. nautobot/circuits/tables.py +39 -1
  16. nautobot/circuits/tests/integration/test_relationships.py +9 -9
  17. nautobot/circuits/tests/test_api.py +4 -8
  18. nautobot/circuits/tests/test_filters.py +10 -4
  19. nautobot/circuits/tests/test_models.py +5 -1
  20. nautobot/circuits/tests/test_views.py +27 -5
  21. nautobot/circuits/views.py +18 -10
  22. nautobot/core/api/__init__.py +8 -2
  23. nautobot/core/api/fields.py +15 -6
  24. nautobot/core/api/filter_backends.py +3 -2
  25. nautobot/core/api/metadata.py +237 -30
  26. nautobot/core/api/mixins.py +94 -0
  27. nautobot/core/api/pagination.py +4 -0
  28. nautobot/core/api/parsers.py +154 -0
  29. nautobot/core/api/renderers.py +153 -2
  30. nautobot/core/api/schema.py +46 -2
  31. nautobot/core/api/serializers.py +377 -35
  32. nautobot/core/api/urls.py +11 -3
  33. nautobot/core/api/utils.py +174 -2
  34. nautobot/core/api/versioning.py +32 -10
  35. nautobot/core/api/views.py +266 -72
  36. nautobot/core/apps/__init__.py +138 -220
  37. nautobot/core/celery/__init__.py +112 -41
  38. nautobot/core/celery/backends.py +19 -12
  39. nautobot/core/celery/control.py +46 -0
  40. nautobot/core/celery/encoders.py +53 -0
  41. nautobot/core/celery/log.py +38 -0
  42. nautobot/core/celery/schedulers.py +23 -4
  43. nautobot/core/celery/task.py +1 -16
  44. nautobot/core/checks.py +0 -27
  45. nautobot/core/choices.py +0 -113
  46. nautobot/core/{cli.py → cli/__init__.py} +1 -1
  47. nautobot/core/cli/__main__.py +3 -0
  48. nautobot/core/constants.py +0 -24
  49. nautobot/core/context_processors.py +12 -0
  50. nautobot/core/filters.py +2 -2
  51. nautobot/core/forms/__init__.py +0 -4
  52. nautobot/core/forms/fields.py +38 -65
  53. nautobot/core/forms/forms.py +4 -1
  54. nautobot/core/forms/utils.py +0 -52
  55. nautobot/core/graphql/schema.py +4 -27
  56. nautobot/core/jobs/__init__.py +75 -0
  57. nautobot/core/management/commands/build_ui.py +255 -0
  58. nautobot/core/management/commands/generate_test_data.py +3 -2
  59. nautobot/core/management/commands/post_upgrade.py +24 -24
  60. nautobot/core/models/__init__.py +26 -1
  61. nautobot/core/models/fields.py +24 -5
  62. nautobot/core/models/generics.py +2 -42
  63. nautobot/core/models/managers.py +5 -0
  64. nautobot/core/models/name_color_content_types.py +0 -14
  65. nautobot/core/models/tree_queries.py +14 -4
  66. nautobot/core/models/utils.py +5 -6
  67. nautobot/core/models/validators.py +17 -8
  68. nautobot/core/releases.py +8 -10
  69. nautobot/core/settings.py +80 -42
  70. nautobot/core/tables.py +5 -5
  71. nautobot/core/tasks.py +4 -7
  72. nautobot/core/templates/base.html +1 -49
  73. nautobot/core/templates/base_django.html +49 -0
  74. nautobot/core/templates/base_react.html +55 -0
  75. nautobot/core/templates/buttons/export.html +6 -4
  76. nautobot/core/templates/generic/object_bulk_create.html +10 -21
  77. nautobot/core/templates/generic/object_list.html +3 -1
  78. nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
  79. nautobot/core/templates/inc/footer.html +1 -0
  80. nautobot/core/templates/inc/javascript.html +0 -14
  81. nautobot/core/templates/inc/nav_menu.html +28 -33
  82. nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
  83. nautobot/core/templates/inc/relationships_table_rows.html +2 -2
  84. nautobot/core/templates/nautobot_config.py.j2 +8 -20
  85. nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
  86. nautobot/core/templates/rest_framework/api.html +8 -0
  87. nautobot/core/templatetags/buttons.py +32 -28
  88. nautobot/core/testing/__init__.py +47 -44
  89. nautobot/core/testing/api.py +362 -47
  90. nautobot/core/testing/filters.py +1 -1
  91. nautobot/core/testing/migrations.py +2 -0
  92. nautobot/core/testing/mixins.py +22 -9
  93. nautobot/core/testing/schema.py +2 -1
  94. nautobot/core/testing/views.py +21 -46
  95. nautobot/core/tests/integration/test_filters.py +17 -8
  96. nautobot/core/tests/integration/test_navbar.py +11 -34
  97. nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
  98. nautobot/core/tests/nautobot_config.py +2 -3
  99. nautobot/core/tests/test_api.py +290 -21
  100. nautobot/core/tests/test_checks.py +0 -7
  101. nautobot/core/tests/test_filters.py +107 -59
  102. nautobot/core/tests/test_forms.py +26 -92
  103. nautobot/core/tests/test_graphql.py +110 -77
  104. nautobot/core/tests/test_logging.py +4 -0
  105. nautobot/core/tests/test_managers.py +3 -1
  106. nautobot/core/tests/test_models.py +2 -0
  107. nautobot/core/tests/test_paginator.py +3 -1
  108. nautobot/core/tests/test_releases.py +12 -12
  109. nautobot/core/tests/test_templatetags_helpers.py +4 -4
  110. nautobot/core/tests/test_utils.py +32 -68
  111. nautobot/core/tests/test_views.py +12 -15
  112. nautobot/core/utils/data.py +17 -0
  113. nautobot/core/utils/deprecation.py +9 -6
  114. nautobot/core/utils/filtering.py +8 -3
  115. nautobot/core/utils/git.py +12 -4
  116. nautobot/core/utils/lookup.py +3 -1
  117. nautobot/core/utils/requests.py +1 -104
  118. nautobot/core/views/__init__.py +1 -0
  119. nautobot/core/views/generic.py +75 -110
  120. nautobot/core/views/mixins.py +52 -61
  121. nautobot/core/views/renderers.py +6 -7
  122. nautobot/core/views/utils.py +80 -0
  123. nautobot/dcim/api/serializers.py +160 -667
  124. nautobot/dcim/api/urls.py +1 -1
  125. nautobot/dcim/api/views.py +7 -44
  126. nautobot/dcim/choices.py +2 -0
  127. nautobot/dcim/filters/__init__.py +21 -0
  128. nautobot/dcim/form_mixins.py +1 -27
  129. nautobot/dcim/forms.py +19 -765
  130. nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -1
  131. nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -13
  132. nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -1
  133. nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -1
  134. nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
  135. nautobot/dcim/migrations/0035_related_name_changes.py +1 -1
  136. nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -1
  137. nautobot/dcim/migrations/0040_tagsfield.py +109 -0
  138. nautobot/dcim/migrations/{0040_ipam__namespaces.py → 0041_ipam__namespaces.py} +1 -1
  139. nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
  140. nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
  141. nautobot/dcim/models/cables.py +3 -33
  142. nautobot/dcim/models/device_component_templates.py +6 -0
  143. nautobot/dcim/models/device_components.py +12 -198
  144. nautobot/dcim/models/devices.py +30 -143
  145. nautobot/dcim/models/locations.py +3 -64
  146. nautobot/dcim/models/power.py +3 -50
  147. nautobot/dcim/models/racks.py +7 -84
  148. nautobot/dcim/navigation.py +141 -467
  149. nautobot/dcim/signals.py +0 -2
  150. nautobot/dcim/tables/locations.py +2 -2
  151. nautobot/dcim/tables/power.py +1 -2
  152. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
  153. nautobot/dcim/templates/dcim/devicetype.html +2 -2
  154. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
  155. nautobot/dcim/templates/dcim/location.html +16 -1
  156. nautobot/dcim/templates/dcim/locationtype.html +15 -0
  157. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
  158. nautobot/dcim/templates/dcim/rackgroup.html +0 -12
  159. nautobot/dcim/tests/test_api.py +166 -81
  160. nautobot/dcim/tests/test_cablepaths.py +41 -35
  161. nautobot/dcim/tests/test_filters.py +67 -23
  162. nautobot/dcim/tests/test_forms.py +5 -205
  163. nautobot/dcim/tests/test_graphql.py +7 -2
  164. nautobot/dcim/tests/test_migrations.py +6 -11
  165. nautobot/dcim/tests/test_models.py +182 -110
  166. nautobot/dcim/tests/test_natural_ordering.py +11 -8
  167. nautobot/dcim/tests/test_signals.py +6 -3
  168. nautobot/dcim/tests/test_views.py +197 -175
  169. nautobot/dcim/urls.py +11 -16
  170. nautobot/dcim/views.py +7 -134
  171. nautobot/docs/additional-features/caching.md +6 -87
  172. nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
  173. nautobot/docs/additional-features/jobs.md +177 -195
  174. nautobot/docs/administration/nautobot-server.md +6 -21
  175. nautobot/docs/administration/replicating-nautobot.md +0 -10
  176. nautobot/docs/configuration/optional-settings.md +32 -41
  177. nautobot/docs/configuration/required-settings.md +11 -52
  178. nautobot/docs/development/application-registry.md +2 -13
  179. nautobot/docs/development/extending-models.md +15 -17
  180. nautobot/docs/development/generic-views.md +0 -2
  181. nautobot/docs/development/getting-started.md +55 -5
  182. nautobot/docs/development/navigation-menu.md +22 -93
  183. nautobot/docs/development/react-ui.md +105 -0
  184. nautobot/docs/development/role-internals.md +1 -3
  185. nautobot/docs/development/style-guide.md +6 -4
  186. nautobot/docs/index.md +3 -2
  187. nautobot/docs/installation/migrating-from-netbox.md +11 -42
  188. nautobot/docs/installation/nautobot.md +1 -1
  189. nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  190. nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  191. nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  192. nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  193. nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
  194. nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  195. nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  196. nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  197. nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  198. nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  199. nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  200. nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  201. nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  202. nautobot/docs/installation/upgrading-from-nautobot-v1.md +170 -747
  203. nautobot/docs/models/dcim/device.md +3 -0
  204. nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
  205. nautobot/docs/models/extras/computedfield.md +4 -4
  206. nautobot/docs/models/extras/gitrepository.md +3 -0
  207. nautobot/docs/models/extras/job.md +1 -0
  208. nautobot/docs/models/extras/jobbutton.md +18 -13
  209. nautobot/docs/models/extras/jobhook.md +7 -4
  210. nautobot/docs/models/extras/jobresult.md +6 -2
  211. nautobot/docs/models/extras/relationship.md +2 -2
  212. nautobot/docs/models/extras/status.md +6 -19
  213. nautobot/docs/models/ipam/ipaddress.md +3 -0
  214. nautobot/docs/models/virtualization/virtualmachine.md +3 -0
  215. nautobot/docs/plugins/development.md +83 -21
  216. nautobot/docs/release-notes/version-1.5.md +53 -0
  217. nautobot/docs/release-notes/version-2.0.md +180 -0
  218. nautobot/docs/requirements.txt +1 -0
  219. nautobot/docs/rest-api/overview.md +384 -215
  220. nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
  221. nautobot/extras/admin.py +3 -5
  222. nautobot/extras/api/customfields.py +15 -39
  223. nautobot/extras/api/fields.py +0 -11
  224. nautobot/extras/api/mixins.py +45 -0
  225. nautobot/extras/api/relationships.py +63 -158
  226. nautobot/extras/api/serializers.py +165 -700
  227. nautobot/extras/api/urls.py +1 -1
  228. nautobot/extras/api/views.py +294 -280
  229. nautobot/extras/apps.py +4 -7
  230. nautobot/extras/choices.py +11 -9
  231. nautobot/extras/constants.py +9 -3
  232. nautobot/extras/datasources/__init__.py +2 -0
  233. nautobot/extras/datasources/git.py +135 -186
  234. nautobot/extras/datasources/registry.py +25 -35
  235. nautobot/extras/filters/__init__.py +20 -19
  236. nautobot/extras/filters/mixins.py +4 -4
  237. nautobot/extras/forms/forms.py +63 -127
  238. nautobot/extras/forms/mixins.py +23 -51
  239. nautobot/extras/health_checks.py +0 -33
  240. nautobot/extras/jobs.py +387 -565
  241. nautobot/extras/management/commands/runjob.py +24 -62
  242. nautobot/extras/managers.py +30 -7
  243. nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
  244. nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -1
  245. nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -1
  246. nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -7
  247. nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -32
  248. nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -1
  249. nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -1
  250. nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
  251. nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -1
  252. nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -2
  253. nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -1
  254. nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -1
  255. nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
  256. nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -1
  257. nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
  258. nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -9
  259. nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -1
  260. nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
  261. nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
  262. 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
  263. nautobot/extras/migrations/{0077_remove_slug.py → 0078_remove_slug.py} +1 -5
  264. nautobot/extras/migrations/0079_tagsfield.py +28 -0
  265. nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
  266. nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
  267. nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
  268. nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
  269. nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
  270. nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
  271. nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
  272. nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
  273. nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
  274. nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
  275. nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
  276. nautobot/extras/models/__init__.py +2 -3
  277. nautobot/extras/models/change_logging.py +0 -36
  278. nautobot/extras/models/customfields.py +39 -33
  279. nautobot/extras/models/datasources.py +48 -50
  280. nautobot/extras/models/groups.py +5 -6
  281. nautobot/extras/models/jobs.py +189 -321
  282. nautobot/extras/models/mixins.py +0 -71
  283. nautobot/extras/models/models.py +0 -19
  284. nautobot/extras/models/relationships.py +19 -13
  285. nautobot/extras/models/roles.py +0 -34
  286. nautobot/extras/models/secrets.py +2 -26
  287. nautobot/extras/models/statuses.py +6 -5
  288. nautobot/extras/models/tags.py +2 -17
  289. nautobot/extras/navigation.py +89 -307
  290. nautobot/extras/plugins/__init__.py +3 -120
  291. nautobot/extras/plugins/utils.py +0 -3
  292. nautobot/extras/plugins/validators.py +5 -4
  293. nautobot/extras/plugins/views.py +16 -3
  294. nautobot/extras/querysets.py +1 -7
  295. nautobot/extras/registry.py +3 -0
  296. nautobot/extras/signals.py +26 -60
  297. nautobot/extras/tables.py +34 -40
  298. nautobot/extras/tasks.py +0 -12
  299. nautobot/extras/templates/extras/configcontext.html +1 -1
  300. nautobot/extras/templates/extras/configcontextschema.html +16 -1
  301. nautobot/extras/templates/extras/customfield.html +0 -13
  302. nautobot/extras/templates/extras/gitrepository.html +3 -3
  303. nautobot/extras/templates/extras/inc/jobresult.html +10 -0
  304. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  305. nautobot/extras/templates/extras/job.html +35 -25
  306. nautobot/extras/templates/extras/job_approval_request.html +15 -30
  307. nautobot/extras/templates/extras/job_detail.html +13 -31
  308. nautobot/extras/templates/extras/job_edit.html +15 -17
  309. nautobot/extras/templates/extras/jobresult.html +24 -6
  310. nautobot/extras/templates/extras/scheduledjob.html +2 -2
  311. nautobot/extras/templates/extras/secret.html +28 -0
  312. nautobot/extras/templatetags/job_buttons.py +1 -0
  313. nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
  314. nautobot/extras/test_jobs/atomic_transaction.py +53 -0
  315. nautobot/extras/test_jobs/dry_run.py +29 -0
  316. nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
  317. nautobot/extras/test_jobs/duplicate_name2.py +9 -0
  318. nautobot/extras/test_jobs/fail.py +23 -0
  319. nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
  320. nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
  321. nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
  322. nautobot/extras/test_jobs/file_upload_pass.py +25 -0
  323. nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
  324. nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
  325. nautobot/extras/test_jobs/job_button_receiver.py +28 -0
  326. nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
  327. nautobot/extras/test_jobs/job_variables.py +88 -0
  328. nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
  329. nautobot/extras/test_jobs/log_redaction.py +20 -0
  330. nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
  331. nautobot/extras/test_jobs/modify_db.py +25 -0
  332. nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
  333. nautobot/extras/test_jobs/object_var_optional.py +21 -0
  334. nautobot/extras/test_jobs/object_var_required.py +21 -0
  335. nautobot/extras/test_jobs/object_vars.py +26 -0
  336. nautobot/extras/test_jobs/pass.py +25 -0
  337. nautobot/extras/test_jobs/profiling.py +32 -0
  338. nautobot/extras/test_jobs/read_only_job.py +15 -0
  339. nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
  340. 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
  341. nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
  342. nautobot/extras/tests/integration/test_computedfields.py +1 -1
  343. nautobot/extras/tests/integration/test_configcontextschema.py +5 -3
  344. nautobot/extras/tests/integration/test_customfields.py +4 -2
  345. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  346. nautobot/extras/tests/integration/test_jobs.py +25 -27
  347. nautobot/extras/tests/integration/test_notes.py +8 -4
  348. nautobot/extras/tests/integration/test_relationships.py +2 -2
  349. nautobot/extras/tests/test_api.py +649 -642
  350. nautobot/extras/tests/test_changelog.py +3 -3
  351. nautobot/extras/tests/test_context_managers.py +5 -3
  352. nautobot/extras/tests/test_customfields.py +92 -50
  353. nautobot/extras/tests/test_datasources.py +189 -112
  354. nautobot/extras/tests/test_dynamicgroups.py +7 -8
  355. nautobot/extras/tests/test_filters.py +137 -89
  356. nautobot/extras/tests/test_forms.py +73 -75
  357. nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
  358. nautobot/extras/tests/test_jobs.py +262 -263
  359. nautobot/extras/tests/test_migrations.py +4 -3
  360. nautobot/extras/tests/test_models.py +116 -161
  361. nautobot/extras/tests/test_plugins.py +38 -60
  362. nautobot/extras/tests/test_relationships.py +167 -120
  363. nautobot/extras/tests/test_tags.py +6 -11
  364. nautobot/extras/tests/test_utils.py +31 -1
  365. nautobot/extras/tests/test_views.py +201 -145
  366. nautobot/extras/tests/test_webhooks.py +6 -2
  367. nautobot/extras/urls.py +42 -42
  368. nautobot/extras/utils.py +137 -163
  369. nautobot/extras/views.py +78 -152
  370. nautobot/ipam/api/fields.py +17 -0
  371. nautobot/ipam/api/serializers.py +58 -164
  372. nautobot/ipam/api/urls.py +1 -1
  373. nautobot/ipam/api/views.py +3 -2
  374. nautobot/ipam/apps.py +1 -2
  375. nautobot/ipam/filters.py +1 -10
  376. nautobot/ipam/forms.py +4 -177
  377. nautobot/ipam/lookups.py +1 -0
  378. nautobot/ipam/management/commands/__init__.py +0 -0
  379. nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
  380. nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -1
  381. nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -38
  382. nautobot/ipam/migrations/0020_related_name_changes.py +1 -1
  383. nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +2 -2
  384. nautobot/ipam/migrations/0028_tagsfield.py +44 -0
  385. nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
  386. nautobot/ipam/migrations/{0028_ipam__namespaces.py → 0030_ipam__namespaces.py} +77 -28
  387. nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
  388. nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
  389. nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
  390. nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
  391. nautobot/ipam/models.py +100 -236
  392. nautobot/ipam/navigation.py +36 -181
  393. nautobot/ipam/querysets.py +20 -25
  394. nautobot/ipam/signals.py +49 -6
  395. nautobot/ipam/tables.py +10 -3
  396. nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
  397. nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
  398. nautobot/ipam/templates/ipam/namespace_retrieve.html +17 -4
  399. nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
  400. nautobot/ipam/templates/ipam/prefix.html +1 -1
  401. nautobot/ipam/templates/ipam/vlangroup.html +0 -13
  402. nautobot/ipam/templates/ipam/vrf_edit.html +6 -0
  403. nautobot/ipam/tests/integration/test_prefixes.py +3 -26
  404. nautobot/ipam/tests/test_api.py +22 -19
  405. nautobot/ipam/tests/test_filters.py +59 -23
  406. nautobot/ipam/tests/test_migrations.py +6 -10
  407. nautobot/ipam/tests/test_models.py +323 -198
  408. nautobot/ipam/tests/test_ordering.py +2 -2
  409. nautobot/ipam/tests/test_querysets.py +44 -24
  410. nautobot/ipam/tests/test_views.py +73 -26
  411. nautobot/ipam/urls.py +16 -0
  412. nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
  413. nautobot/ipam/utils/migrations.py +713 -0
  414. nautobot/ipam/views.py +137 -20
  415. nautobot/project-static/docs/404.html +1178 -10
  416. nautobot/project-static/docs/additional-features/caching.html +1224 -159
  417. nautobot/project-static/docs/additional-features/change-logging.html +1180 -12
  418. nautobot/project-static/docs/additional-features/config-contexts.html +1180 -12
  419. nautobot/project-static/docs/additional-features/graphql.html +1179 -11
  420. nautobot/project-static/docs/additional-features/healthcheck.html +1180 -12
  421. nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1184 -12
  422. nautobot/project-static/docs/additional-features/jobs.html +1514 -328
  423. nautobot/project-static/docs/additional-features/napalm.html +1180 -12
  424. nautobot/project-static/docs/additional-features/prometheus-metrics.html +1180 -12
  425. nautobot/project-static/docs/additional-features/template-filters.html +1180 -12
  426. nautobot/project-static/docs/administration/celery-queues.html +1178 -10
  427. nautobot/project-static/docs/administration/nautobot-server.html +1451 -304
  428. nautobot/project-static/docs/administration/nautobot-shell.html +1178 -10
  429. nautobot/project-static/docs/administration/permissions.html +1178 -10
  430. nautobot/project-static/docs/administration/replicating-nautobot.html +1262 -113
  431. nautobot/project-static/docs/apps/index.html +1178 -10
  432. nautobot/project-static/docs/apps/nautobot-apps.html +1178 -10
  433. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1580 -426
  434. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1178 -10
  435. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3481 -1838
  436. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1178 -10
  437. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1178 -10
  438. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1185 -11
  439. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1719 -551
  440. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2062 -930
  441. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +1946 -659
  442. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1180 -12
  443. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1189 -21
  444. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9283 -6218
  445. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2734 -2122
  446. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1178 -10
  447. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2337 -1300
  448. nautobot/project-static/docs/configuration/authentication/ldap.html +1178 -10
  449. nautobot/project-static/docs/configuration/authentication/remote.html +1178 -10
  450. nautobot/project-static/docs/configuration/authentication/sso.html +1178 -10
  451. nautobot/project-static/docs/configuration/index.html +1178 -10
  452. nautobot/project-static/docs/configuration/optional-settings.html +1311 -160
  453. nautobot/project-static/docs/configuration/required-settings.html +1312 -211
  454. nautobot/project-static/docs/core-functionality/circuits.html +1178 -10
  455. nautobot/project-static/docs/core-functionality/device-types.html +1178 -10
  456. nautobot/project-static/docs/core-functionality/devices.html +1182 -10
  457. nautobot/project-static/docs/core-functionality/ipam.html +1182 -10
  458. nautobot/project-static/docs/core-functionality/power.html +1178 -10
  459. nautobot/project-static/docs/core-functionality/secrets.html +1178 -10
  460. nautobot/project-static/docs/core-functionality/services.html +1178 -10
  461. nautobot/project-static/docs/core-functionality/sites-and-racks.html +1178 -10
  462. nautobot/project-static/docs/core-functionality/tenancy.html +1178 -10
  463. nautobot/project-static/docs/core-functionality/virtualization.html +1182 -10
  464. nautobot/project-static/docs/core-functionality/vlans.html +1179 -11
  465. nautobot/project-static/docs/development/application-registry.html +1190 -42
  466. nautobot/project-static/docs/development/best-practices.html +1178 -10
  467. nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1178 -10
  468. nautobot/project-static/docs/development/extending-models.html +1238 -83
  469. nautobot/project-static/docs/development/generic-views.html +1180 -14
  470. nautobot/project-static/docs/development/getting-started.html +1365 -90
  471. nautobot/project-static/docs/development/homepage.html +1178 -10
  472. nautobot/project-static/docs/development/index.html +1178 -10
  473. nautobot/project-static/docs/development/model-features.html +1178 -10
  474. nautobot/project-static/docs/development/natural-keys.html +1178 -10
  475. nautobot/project-static/docs/development/navigation-menu.html +1215 -125
  476. nautobot/project-static/docs/development/react-ui.html +4199 -0
  477. nautobot/project-static/docs/development/release-checklist.html +1178 -10
  478. nautobot/project-static/docs/development/role-internals.html +1179 -12
  479. nautobot/project-static/docs/development/style-guide.html +1188 -19
  480. nautobot/project-static/docs/development/templates.html +1178 -10
  481. nautobot/project-static/docs/development/testing.html +1178 -10
  482. nautobot/project-static/docs/development/user-preferences.html +1178 -10
  483. nautobot/project-static/docs/docker/index.html +1178 -10
  484. nautobot/project-static/docs/index.html +1183 -12
  485. nautobot/project-static/docs/installation/centos.html +1178 -10
  486. nautobot/project-static/docs/installation/external-authentication.html +1178 -10
  487. nautobot/project-static/docs/installation/http-server.html +1178 -10
  488. nautobot/project-static/docs/installation/index.html +1178 -10
  489. nautobot/project-static/docs/installation/migrating-from-netbox.html +1305 -189
  490. nautobot/project-static/docs/installation/migrating-from-postgresql.html +1178 -10
  491. nautobot/project-static/docs/installation/nautobot.html +1179 -11
  492. nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1178 -10
  493. nautobot/project-static/docs/installation/selinux-troubleshooting.html +1178 -10
  494. nautobot/project-static/docs/installation/services.html +1178 -10
  495. nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  496. nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  497. nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  498. nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  499. nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
  500. nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  501. nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  502. nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  503. nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  504. nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  505. nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  506. nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  507. nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  508. nautobot/project-static/docs/installation/ubuntu.html +1178 -10
  509. nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +3823 -2152
  510. nautobot/project-static/docs/installation/upgrading.html +1178 -10
  511. nautobot/project-static/docs/models/circuits/circuit.html +1293 -103
  512. nautobot/project-static/docs/models/circuits/circuittermination.html +1293 -103
  513. nautobot/project-static/docs/models/circuits/circuittype.html +1293 -103
  514. nautobot/project-static/docs/models/circuits/provider.html +1293 -103
  515. nautobot/project-static/docs/models/circuits/providernetwork.html +1293 -103
  516. nautobot/project-static/docs/models/dcim/cable.html +1324 -103
  517. nautobot/project-static/docs/models/dcim/consoleport.html +1293 -103
  518. nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1293 -103
  519. nautobot/project-static/docs/models/dcim/consoleserverport.html +1293 -103
  520. nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1293 -103
  521. nautobot/project-static/docs/models/dcim/device.html +1326 -132
  522. nautobot/project-static/docs/models/dcim/devicebay.html +1293 -103
  523. nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1293 -103
  524. nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1379 -97
  525. nautobot/project-static/docs/models/dcim/devicetype.html +1293 -103
  526. nautobot/project-static/docs/models/dcim/frontport.html +1293 -103
  527. nautobot/project-static/docs/models/dcim/frontporttemplate.html +1293 -103
  528. nautobot/project-static/docs/models/dcim/interface.html +1293 -103
  529. nautobot/project-static/docs/models/dcim/interfacetemplate.html +1293 -103
  530. nautobot/project-static/docs/models/dcim/inventoryitem.html +1293 -103
  531. nautobot/project-static/docs/models/dcim/location.html +1293 -103
  532. nautobot/project-static/docs/models/dcim/locationtype.html +1293 -103
  533. nautobot/project-static/docs/models/dcim/manufacturer.html +1292 -102
  534. nautobot/project-static/docs/models/dcim/platform.html +1272 -82
  535. nautobot/project-static/docs/models/dcim/powerfeed.html +1270 -80
  536. nautobot/project-static/docs/models/dcim/poweroutlet.html +1272 -82
  537. nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1272 -82
  538. nautobot/project-static/docs/models/dcim/powerpanel.html +1270 -80
  539. nautobot/project-static/docs/models/dcim/powerport.html +1272 -82
  540. nautobot/project-static/docs/models/dcim/powerporttemplate.html +1272 -82
  541. nautobot/project-static/docs/models/dcim/rack.html +1272 -82
  542. nautobot/project-static/docs/models/dcim/rackgroup.html +1272 -82
  543. nautobot/project-static/docs/models/dcim/rackreservation.html +1272 -82
  544. nautobot/project-static/docs/models/dcim/rearport.html +1286 -96
  545. nautobot/project-static/docs/models/dcim/rearporttemplate.html +1286 -96
  546. nautobot/project-static/docs/models/dcim/region.html +1178 -10
  547. nautobot/project-static/docs/models/dcim/site.html +1178 -10
  548. nautobot/project-static/docs/models/dcim/virtualchassis.html +1284 -94
  549. nautobot/project-static/docs/models/extras/computedfield.html +1184 -16
  550. nautobot/project-static/docs/models/extras/configcontext.html +1314 -86
  551. nautobot/project-static/docs/models/extras/configcontextschema.html +1276 -86
  552. nautobot/project-static/docs/models/extras/customfield.html +1180 -12
  553. nautobot/project-static/docs/models/extras/customlink.html +1180 -12
  554. nautobot/project-static/docs/models/extras/dynamicgroup.html +1180 -12
  555. nautobot/project-static/docs/models/extras/exporttemplate.html +1180 -12
  556. nautobot/project-static/docs/models/extras/gitrepository.html +1184 -12
  557. nautobot/project-static/docs/models/extras/graphqlquery.html +1321 -86
  558. nautobot/project-static/docs/models/extras/imageattachment.html +1276 -86
  559. nautobot/project-static/docs/models/extras/job.html +1277 -86
  560. nautobot/project-static/docs/models/extras/jobbutton.html +1201 -29
  561. nautobot/project-static/docs/models/extras/jobhook.html +1188 -16
  562. nautobot/project-static/docs/models/extras/joblogentry.html +1274 -84
  563. nautobot/project-static/docs/models/extras/jobresult.html +1364 -169
  564. nautobot/project-static/docs/models/extras/note.html +1180 -12
  565. nautobot/project-static/docs/models/extras/relationship.html +1182 -14
  566. nautobot/project-static/docs/models/extras/role.html +1320 -86
  567. nautobot/project-static/docs/models/extras/secret.html +1314 -86
  568. nautobot/project-static/docs/models/extras/secretsgroup.html +1276 -86
  569. nautobot/project-static/docs/models/extras/status.html +1188 -59
  570. nautobot/project-static/docs/models/extras/tag.html +1180 -12
  571. nautobot/project-static/docs/models/extras/webhook.html +1180 -12
  572. nautobot/project-static/docs/models/ipam/ipaddress.html +1327 -102
  573. nautobot/project-static/docs/models/ipam/prefix.html +1276 -86
  574. nautobot/project-static/docs/models/ipam/rir.html +1276 -86
  575. nautobot/project-static/docs/models/ipam/routetarget.html +1276 -86
  576. nautobot/project-static/docs/models/ipam/service.html +1276 -86
  577. nautobot/project-static/docs/models/ipam/vlan.html +1276 -86
  578. nautobot/project-static/docs/models/ipam/vlangroup.html +1276 -86
  579. nautobot/project-static/docs/models/ipam/vrf.html +1276 -86
  580. nautobot/project-static/docs/models/tenancy/tenant.html +1276 -86
  581. nautobot/project-static/docs/models/tenancy/tenantgroup.html +1276 -86
  582. nautobot/project-static/docs/models/users/objectpermission.html +1314 -86
  583. nautobot/project-static/docs/models/users/token.html +1276 -86
  584. nautobot/project-static/docs/models/virtualization/cluster.html +1276 -86
  585. nautobot/project-static/docs/models/virtualization/clustergroup.html +1276 -86
  586. nautobot/project-static/docs/models/virtualization/clustertype.html +1276 -86
  587. nautobot/project-static/docs/models/virtualization/virtualmachine.html +1321 -127
  588. nautobot/project-static/docs/models/virtualization/vminterface.html +1276 -86
  589. nautobot/project-static/docs/objects.inv +0 -0
  590. nautobot/project-static/docs/plugins/development.html +1726 -495
  591. nautobot/project-static/docs/plugins/index.html +1178 -10
  592. nautobot/project-static/docs/plugins/porting-from-netbox.html +1178 -10
  593. nautobot/project-static/docs/release-notes/index.html +1178 -10
  594. nautobot/project-static/docs/release-notes/version-1.0.html +1178 -10
  595. nautobot/project-static/docs/release-notes/version-1.1.html +1178 -10
  596. nautobot/project-static/docs/release-notes/version-1.2.html +1178 -10
  597. nautobot/project-static/docs/release-notes/version-1.3.html +1178 -10
  598. nautobot/project-static/docs/release-notes/version-1.4.html +1178 -10
  599. nautobot/project-static/docs/release-notes/version-1.5.html +1608 -225
  600. nautobot/project-static/docs/release-notes/version-2.0.html +1547 -47
  601. nautobot/project-static/docs/requirements.txt +1 -0
  602. nautobot/project-static/docs/rest-api/authentication.html +1179 -11
  603. nautobot/project-static/docs/rest-api/filtering.html +1178 -10
  604. nautobot/project-static/docs/rest-api/overview.html +1841 -446
  605. nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
  606. nautobot/project-static/docs/search/search_index.json +1 -1
  607. nautobot/project-static/docs/sitemap.xml +197 -187
  608. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  609. nautobot/project-static/docs/user-guides/custom-fields.html +1178 -10
  610. nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1178 -10
  611. nautobot/project-static/docs/user-guides/getting-started/index.html +1178 -10
  612. nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1178 -10
  613. nautobot/project-static/docs/user-guides/getting-started/ipam.html +1178 -10
  614. nautobot/project-static/docs/user-guides/getting-started/platforms.html +1178 -10
  615. nautobot/project-static/docs/user-guides/getting-started/regions.html +1178 -10
  616. nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1178 -10
  617. nautobot/project-static/docs/user-guides/getting-started/tenants.html +1178 -10
  618. nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1178 -10
  619. nautobot/project-static/docs/user-guides/git-data-source.html +1178 -10
  620. nautobot/project-static/docs/user-guides/graphql.html +1178 -10
  621. nautobot/project-static/docs/user-guides/relationships.html +1178 -10
  622. nautobot/project-static/docs/user-guides/s3-django-storage.html +1178 -10
  623. nautobot/project-static/js/forms.js +16 -9
  624. nautobot/project-static/js/theme.js +5 -0
  625. nautobot/tenancy/api/serializers.py +4 -32
  626. nautobot/tenancy/api/urls.py +1 -1
  627. nautobot/tenancy/forms.py +0 -28
  628. nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
  629. nautobot/tenancy/models.py +0 -25
  630. nautobot/tenancy/navigation.py +6 -39
  631. nautobot/tenancy/templates/tenancy/tenant.html +12 -12
  632. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  633. nautobot/tenancy/tests/test_api.py +1 -3
  634. nautobot/tenancy/tests/test_filters.py +10 -5
  635. nautobot/tenancy/views.py +0 -2
  636. nautobot/ui/.eslintignore +6 -0
  637. nautobot/ui/.gitignore +10 -0
  638. nautobot/ui/.prettierignore +9 -0
  639. nautobot/ui/.prettierrc +4 -0
  640. nautobot/ui/README.md +33 -0
  641. nautobot/ui/app_imports.js.j2 +7 -0
  642. nautobot/ui/craco.config.js +46 -0
  643. nautobot/ui/jsconfig-base.json +11 -0
  644. nautobot/ui/jsconfig.json +5 -0
  645. nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
  646. nautobot/ui/package-lock.json +21451 -0
  647. nautobot/ui/package.json +70 -0
  648. nautobot/ui/public/index.html +47 -0
  649. nautobot/ui/public/logo192.png +0 -0
  650. nautobot/ui/public/logo512.png +0 -0
  651. nautobot/ui/public/manifest.json +25 -0
  652. nautobot/ui/public/nautobot_logo.svg +131 -0
  653. nautobot/ui/public/robots.txt +3 -0
  654. nautobot/ui/src/App.js +71 -0
  655. nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
  656. nautobot/ui/src/components/AppTab.js +40 -0
  657. nautobot/ui/src/components/Apps.js +60 -0
  658. nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
  659. nautobot/ui/src/components/HomePanel.js +58 -0
  660. nautobot/ui/src/components/JobHistoryTable.js +78 -0
  661. nautobot/ui/src/components/Layout.js +53 -0
  662. nautobot/ui/src/components/LoadingWidget.js +25 -0
  663. nautobot/ui/src/components/Navbar.js +116 -0
  664. nautobot/ui/src/components/NotificationPopover.js +27 -0
  665. nautobot/ui/src/components/ObjectListTable.js +209 -0
  666. nautobot/ui/src/components/ReferenceDataTag.js +35 -0
  667. nautobot/ui/src/components/RouterButton.js +10 -0
  668. nautobot/ui/src/components/RouterLink.js +10 -0
  669. nautobot/ui/src/components/SidebarNav.js +147 -0
  670. nautobot/ui/src/components/Table.js +48 -0
  671. nautobot/ui/src/components/TableItem.js +71 -0
  672. nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
  673. nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
  674. nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
  675. nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
  676. nautobot/ui/src/components/__tests__/Table.test.js +36 -0
  677. nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
  678. nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
  679. nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
  680. nautobot/ui/src/components/pagination.js +93 -0
  681. nautobot/ui/src/components/paginator.js +79 -0
  682. nautobot/ui/src/components/paginator_form.js +43 -0
  683. nautobot/ui/src/components/usePagination.js +57 -0
  684. nautobot/ui/src/constants/apiPath.js +10 -0
  685. nautobot/ui/src/constants/icons.js +15 -0
  686. nautobot/ui/src/constants/size.js +15 -0
  687. nautobot/ui/src/index.js +65 -0
  688. nautobot/ui/src/reportWebVitals.js +15 -0
  689. nautobot/ui/src/router.js +77 -0
  690. nautobot/ui/src/utils/api.js +131 -0
  691. nautobot/ui/src/utils/app-import.js +15 -0
  692. nautobot/ui/src/utils/color.js +15 -0
  693. nautobot/ui/src/utils/date.js +14 -0
  694. nautobot/ui/src/utils/index.js +15 -0
  695. nautobot/ui/src/utils/navigation.js +32 -0
  696. nautobot/ui/src/utils/session.js +64 -0
  697. nautobot/ui/src/utils/store.js +242 -0
  698. nautobot/ui/src/utils/string.js +6 -0
  699. nautobot/ui/src/utils/url.js +4 -0
  700. nautobot/ui/src/views/Home.js +138 -0
  701. nautobot/ui/src/views/InstalledApps.js +80 -0
  702. nautobot/ui/src/views/Login.js +48 -0
  703. nautobot/ui/src/views/Logout.js +20 -0
  704. nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
  705. nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
  706. nautobot/ui/src/views/__tests__/Login.test.js +15 -0
  707. nautobot/ui/src/views/generic/GenericView.js +142 -0
  708. nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
  709. nautobot/ui/src/views/generic/ObjectList.js +127 -0
  710. nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
  711. nautobot/users/admin.py +1 -1
  712. nautobot/users/api/serializers.py +51 -61
  713. nautobot/users/api/urls.py +1 -1
  714. nautobot/users/api/views.py +53 -2
  715. nautobot/users/tests/test_api.py +110 -25
  716. nautobot/virtualization/api/serializers.py +18 -130
  717. nautobot/virtualization/api/urls.py +1 -1
  718. nautobot/virtualization/api/views.py +1 -22
  719. nautobot/virtualization/forms.py +13 -99
  720. nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -1
  721. nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -11
  722. nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -1
  723. nautobot/virtualization/migrations/0018_related_name_changes.py +1 -1
  724. nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
  725. nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
  726. nautobot/virtualization/migrations/{0021_ipam__namespaces.py → 0023_ipam__namespaces.py} +2 -2
  727. nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
  728. nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
  729. nautobot/virtualization/models.py +31 -123
  730. nautobot/virtualization/navigation.py +18 -99
  731. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -1
  732. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
  733. nautobot/virtualization/tests/test_api.py +25 -26
  734. nautobot/virtualization/tests/test_filters.py +41 -15
  735. nautobot/virtualization/tests/test_models.py +31 -7
  736. nautobot/virtualization/tests/test_views.py +42 -25
  737. nautobot/virtualization/views.py +7 -6
  738. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +3 -7
  739. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +744 -602
  740. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
  741. nautobot/circuits/api/nested_serializers.py +0 -69
  742. nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
  743. nautobot/dcim/api/nested_serializers.py +0 -356
  744. nautobot/dcim/templates/dcim/device_import.html +0 -5
  745. nautobot/dcim/templates/dcim/device_import_child.html +0 -5
  746. nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
  747. nautobot/extras/api/nested_serializers.py +0 -353
  748. nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -41
  749. nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -46
  750. nautobot/extras/reports.py +0 -60
  751. nautobot/extras/scripts.py +0 -72
  752. nautobot/extras/tests/example_jobs/script_variables.py +0 -67
  753. nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
  754. nautobot/extras/tests/example_jobs/test_fail.py +0 -16
  755. nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
  756. nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
  757. nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
  758. nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
  759. nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
  760. nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
  761. nautobot/extras/tests/example_jobs/test_modify_db.py +0 -18
  762. nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
  763. nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
  764. nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
  765. nautobot/extras/tests/example_jobs/test_pass.py +0 -19
  766. nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
  767. nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
  768. nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
  769. nautobot/ipam/api/nested_serializers.py +0 -159
  770. nautobot/ipam/migrations/0029_ipam__prefix__add_parent.py +0 -31
  771. nautobot/ipam/migrations/0030_ipam__prefix__data_migration.py +0 -13
  772. nautobot/ipam/migrations/0031_ipam__ipaddress__add_parent.py +0 -41
  773. nautobot/ipam/migrations/0032_ipam__ipaddress__data_migration.py +0 -11
  774. nautobot/tenancy/api/nested_serializers.py +0 -31
  775. nautobot/users/api/nested_serializers.py +0 -67
  776. nautobot/virtualization/api/nested_serializers.py +0 -65
  777. /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
  778. /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
  779. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
  780. {nautobot-2.0.0a3.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,11 +43,8 @@ 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
  )
@@ -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,35 +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
- # Limit rack_group queryset by assigned location
547
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
548
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
549
-
550
-
551
440
  class RackBulkEditForm(
552
441
  TagsBulkEditFormMixin,
553
442
  LocatableModelBulkEditFormMixin,
@@ -694,47 +583,6 @@ class RackReservationForm(NautobotModelForm, TenancyForm):
694
583
  ]
695
584
 
696
585
 
697
- class RackReservationCSVForm(CustomFieldModelCSVForm):
698
- location = CSVModelChoiceField(queryset=Location.objects.all(), to_field_name="name", help_text="Parent location")
699
- rack_group = CSVModelChoiceField(
700
- queryset=RackGroup.objects.all(),
701
- to_field_name="name",
702
- required=False,
703
- help_text="Rack's group (if any)",
704
- )
705
- rack = CSVModelChoiceField(queryset=Rack.objects.all(), to_field_name="name", help_text="Rack")
706
- units = SimpleArrayField(
707
- base_field=forms.IntegerField(),
708
- required=True,
709
- help_text="Comma-separated list of individual unit numbers",
710
- )
711
- tenant = CSVModelChoiceField(
712
- queryset=Tenant.objects.all(),
713
- required=False,
714
- to_field_name="name",
715
- help_text="Assigned tenant",
716
- )
717
-
718
- class Meta:
719
- model = RackReservation
720
- fields = ("location", "rack_group", "rack", "units", "tenant", "description")
721
-
722
- def __init__(self, data=None, *args, **kwargs):
723
- super().__init__(data, *args, **kwargs)
724
-
725
- if data:
726
- # Limit rack_group queryset by assigned location
727
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
728
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
729
-
730
- # Limit rack queryset by assigned location and group
731
- params = {
732
- f"location__{self.fields['location'].to_field_name}": data.get("location"),
733
- f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
734
- }
735
- self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
736
-
737
-
738
586
  class RackReservationBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
739
587
  pk = forms.ModelMultipleChoiceField(queryset=RackReservation.objects.all(), widget=forms.MultipleHiddenInput())
740
588
  user = forms.ModelChoiceField(
@@ -791,12 +639,6 @@ class ManufacturerForm(NautobotModelForm):
791
639
  ]
792
640
 
793
641
 
794
- class ManufacturerCSVForm(CustomFieldModelCSVForm):
795
- class Meta:
796
- model = Manufacturer
797
- fields = Manufacturer.csv_headers
798
-
799
-
800
642
  #
801
643
  # Device types
802
644
  #
@@ -835,6 +677,14 @@ class DeviceTypeForm(NautobotModelForm):
835
677
 
836
678
 
837
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
+
838
688
  manufacturer = forms.ModelChoiceField(queryset=Manufacturer.objects.all(), to_field_name="name")
839
689
 
840
690
  class Meta:
@@ -1403,6 +1253,14 @@ class DeviceBayTemplateBulkEditForm(NautobotBulkEditForm):
1403
1253
 
1404
1254
 
1405
1255
  class ComponentTemplateImportForm(BootstrapMixin, CustomFieldModelCSVForm):
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
+ """
1263
+
1406
1264
  def __init__(self, device_type, data=None, *args, **kwargs):
1407
1265
  # Must pass the parent DeviceType on form initialization
1408
1266
  data.update(
@@ -1573,19 +1431,6 @@ class PlatformForm(NautobotModelForm):
1573
1431
  }
1574
1432
 
1575
1433
 
1576
- class PlatformCSVForm(CustomFieldModelCSVForm):
1577
- manufacturer = CSVModelChoiceField(
1578
- queryset=Manufacturer.objects.all(),
1579
- required=False,
1580
- to_field_name="name",
1581
- help_text="Limit platform assignments to this manufacturer",
1582
- )
1583
-
1584
- class Meta:
1585
- model = Platform
1586
- fields = Platform.csv_headers
1587
-
1588
-
1589
1434
  #
1590
1435
  # Devices
1591
1436
  #
@@ -1768,164 +1613,6 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
1768
1613
  return instance
1769
1614
 
1770
1615
 
1771
- class BaseDeviceCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
1772
- tenant = CSVModelChoiceField(
1773
- queryset=Tenant.objects.all(),
1774
- required=False,
1775
- to_field_name="name",
1776
- help_text="Assigned tenant",
1777
- )
1778
- manufacturer = CSVModelChoiceField(
1779
- queryset=Manufacturer.objects.all(),
1780
- to_field_name="name",
1781
- help_text="Device type manufacturer",
1782
- )
1783
- device_type = CSVModelChoiceField(
1784
- queryset=DeviceType.objects.all(),
1785
- to_field_name="model",
1786
- help_text="Device type model",
1787
- )
1788
- platform = CSVModelChoiceField(
1789
- queryset=Platform.objects.all(),
1790
- required=False,
1791
- to_field_name="name",
1792
- help_text="Assigned platform",
1793
- )
1794
- cluster = CSVModelChoiceField(
1795
- queryset=Cluster.objects.all(),
1796
- to_field_name="name",
1797
- required=False,
1798
- help_text="Virtualization cluster",
1799
- )
1800
- secrets_group = CSVModelChoiceField(
1801
- queryset=SecretsGroup.objects.all(),
1802
- required=False,
1803
- to_field_name="name",
1804
- help_text="Secrets group",
1805
- )
1806
-
1807
- class Meta:
1808
- fields = []
1809
- model = Device
1810
-
1811
- def __init__(self, data=None, *args, **kwargs):
1812
- super().__init__(data, *args, **kwargs)
1813
-
1814
- if data:
1815
- # Limit device type queryset by manufacturer
1816
- params = {f"manufacturer__{self.fields['manufacturer'].to_field_name}": data.get("manufacturer")}
1817
- self.fields["device_type"].queryset = self.fields["device_type"].queryset.filter(**params)
1818
-
1819
-
1820
- class DeviceCSVForm(LocatableModelCSVFormMixin, BaseDeviceCSVForm, RoleRequiredRoleModelCSVFormMixin):
1821
- rack_group = CSVModelChoiceField(
1822
- queryset=RackGroup.objects.all(),
1823
- to_field_name="name",
1824
- required=False,
1825
- help_text="Rack's group (if any)",
1826
- )
1827
- rack = CSVModelChoiceField(
1828
- queryset=Rack.objects.all(),
1829
- to_field_name="name",
1830
- required=False,
1831
- help_text="Assigned rack",
1832
- )
1833
- face = CSVChoiceField(choices=DeviceFaceChoices, required=False, help_text="Mounted rack face")
1834
- device_redundancy_group = CSVModelChoiceField(
1835
- queryset=DeviceRedundancyGroup.objects.all(),
1836
- to_field_name="slug",
1837
- required=False,
1838
- help_text="Associated device redundancy group (slug)",
1839
- )
1840
-
1841
- class Meta(BaseDeviceCSVForm.Meta):
1842
- fields = [
1843
- "name",
1844
- "role",
1845
- "tenant",
1846
- "manufacturer",
1847
- "device_type",
1848
- "platform",
1849
- "serial",
1850
- "asset_tag",
1851
- "status",
1852
- "location",
1853
- "rack_group",
1854
- "rack",
1855
- "position",
1856
- "face",
1857
- "device_redundancy_group",
1858
- "device_redundancy_group_priority",
1859
- "cluster",
1860
- "comments",
1861
- ]
1862
-
1863
- def __init__(self, data=None, *args, **kwargs):
1864
- super().__init__(data, *args, **kwargs)
1865
-
1866
- if data:
1867
- # Limit rack_group queryset by assigned location
1868
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
1869
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
1870
-
1871
- # Limit rack queryset by assigned location and group
1872
- params = {
1873
- f"location__{self.fields['location'].to_field_name}": data.get("location"),
1874
- f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
1875
- }
1876
- self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
1877
-
1878
- # 2.0 TODO: limit location queryset by assigned location
1879
-
1880
-
1881
- class ChildDeviceCSVForm(BaseDeviceCSVForm):
1882
- parent = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Parent device")
1883
- device_bay = CSVModelChoiceField(
1884
- queryset=DeviceBay.objects.all(),
1885
- to_field_name="name",
1886
- help_text="Device bay in which this device is installed",
1887
- )
1888
-
1889
- class Meta(BaseDeviceCSVForm.Meta):
1890
- fields = [
1891
- "name",
1892
- "role",
1893
- "tenant",
1894
- "manufacturer",
1895
- "device_type",
1896
- "platform",
1897
- "serial",
1898
- "asset_tag",
1899
- "status",
1900
- "parent",
1901
- "device_bay",
1902
- "cluster",
1903
- "comments",
1904
- ]
1905
-
1906
- def __init__(self, data=None, *args, **kwargs):
1907
- super().__init__(data, *args, **kwargs)
1908
-
1909
- if data:
1910
- # Limit device bay queryset by parent device
1911
- params = {f"device__{self.fields['parent'].to_field_name}": data.get("parent")}
1912
- self.fields["device_bay"].queryset = self.fields["device_bay"].queryset.filter(**params)
1913
-
1914
- def clean(self):
1915
- super().clean()
1916
-
1917
- # Set parent_bay reverse relationship
1918
- device_bay = self.cleaned_data.get("device_bay")
1919
- if device_bay:
1920
- self.instance.parent_bay = device_bay
1921
-
1922
- # Inherit location and rack from parent device
1923
- parent = self.cleaned_data.get("parent")
1924
- if parent:
1925
- self.instance.location = parent.location
1926
- self.instance.rack = parent.rack
1927
-
1928
-
1929
1616
  class DeviceBulkEditForm(
1930
1617
  TagsBulkEditFormMixin,
1931
1618
  LocatableModelBulkEditFormMixin,
@@ -2173,15 +1860,6 @@ class ConsolePortBulkEditForm(
2173
1860
  nullable_fields = ["label", "description"]
2174
1861
 
2175
1862
 
2176
- class ConsolePortCSVForm(CustomFieldModelCSVForm):
2177
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2178
- type = CSVChoiceField(choices=ConsolePortTypeChoices, required=False, help_text="Port type")
2179
-
2180
- class Meta:
2181
- model = ConsolePort
2182
- fields = ConsolePort.csv_headers
2183
-
2184
-
2185
1863
  #
2186
1864
  # Console server ports
2187
1865
  #
@@ -2240,15 +1918,6 @@ class ConsoleServerPortBulkEditForm(
2240
1918
  nullable_fields = ["label", "description"]
2241
1919
 
2242
1920
 
2243
- class ConsoleServerPortCSVForm(CustomFieldModelCSVForm):
2244
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2245
- type = CSVChoiceField(choices=ConsolePortTypeChoices, required=False, help_text="Port type")
2246
-
2247
- class Meta:
2248
- model = ConsoleServerPort
2249
- fields = ConsoleServerPort.csv_headers
2250
-
2251
-
2252
1921
  #
2253
1922
  # Power ports
2254
1923
  #
@@ -2324,15 +1993,6 @@ class PowerPortBulkEditForm(
2324
1993
  nullable_fields = ["label", "description"]
2325
1994
 
2326
1995
 
2327
- class PowerPortCSVForm(CustomFieldModelCSVForm):
2328
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2329
- type = CSVChoiceField(choices=PowerPortTypeChoices, required=False, help_text="Port type")
2330
-
2331
- class Meta:
2332
- model = PowerPort
2333
- fields = PowerPort.csv_headers
2334
-
2335
-
2336
1996
  #
2337
1997
  # Power outlets
2338
1998
  #
@@ -2437,46 +2097,6 @@ class PowerOutletBulkEditForm(
2437
2097
  self.fields["power_port"].widget.attrs["disabled"] = True
2438
2098
 
2439
2099
 
2440
- class PowerOutletCSVForm(CustomFieldModelCSVForm):
2441
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2442
- type = CSVChoiceField(choices=PowerOutletTypeChoices, required=False, help_text="Outlet type")
2443
- power_port = CSVModelChoiceField(
2444
- queryset=PowerPort.objects.all(),
2445
- required=False,
2446
- to_field_name="name",
2447
- help_text="Local power port which feeds this outlet",
2448
- )
2449
- feed_leg = CSVChoiceField(
2450
- choices=PowerOutletFeedLegChoices,
2451
- required=False,
2452
- help_text="Electrical phase (for three-phase circuits)",
2453
- )
2454
-
2455
- class Meta:
2456
- model = PowerOutlet
2457
- fields = PowerOutlet.csv_headers
2458
-
2459
- def __init__(self, *args, **kwargs):
2460
- super().__init__(*args, **kwargs)
2461
-
2462
- # Limit PowerPort choices to those belonging to this device (or VC master)
2463
- if self.is_bound:
2464
- try:
2465
- device = self.fields["device"].to_python(self.data["device"])
2466
- except forms.ValidationError:
2467
- device = None
2468
- else:
2469
- try:
2470
- device = self.instance.device
2471
- except Device.DoesNotExist:
2472
- device = None
2473
-
2474
- if device:
2475
- self.fields["power_port"].queryset = PowerPort.objects.filter(device__in=[device, device.get_vc_master()])
2476
- else:
2477
- self.fields["power_port"].queryset = PowerPort.objects.none()
2478
-
2479
-
2480
2100
  #
2481
2101
  # Interfaces
2482
2102
  #
@@ -2520,7 +2140,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
2520
2140
  queryset=VLAN.objects.all(),
2521
2141
  required=False,
2522
2142
  label="Untagged VLAN",
2523
- brief_mode=False,
2524
2143
  query_params={
2525
2144
  "location": "null",
2526
2145
  },
@@ -2529,7 +2148,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
2529
2148
  queryset=VLAN.objects.all(),
2530
2149
  required=False,
2531
2150
  label="Tagged VLANs",
2532
- brief_mode=False,
2533
2151
  query_params={
2534
2152
  "location": "null",
2535
2153
  },
@@ -2538,10 +2156,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
2538
2156
  queryset=IPAddress.objects.all(),
2539
2157
  required=False,
2540
2158
  label="IP Addresses",
2541
- brief_mode=False,
2542
- query_params={
2543
- "vrf": "$vrf",
2544
- },
2545
2159
  )
2546
2160
 
2547
2161
  class Meta:
@@ -2665,7 +2279,6 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
2665
2279
  untagged_vlan = DynamicModelChoiceField(
2666
2280
  queryset=VLAN.objects.all(),
2667
2281
  required=False,
2668
- brief_mode=False,
2669
2282
  query_params={
2670
2283
  "available_on_device": "$device",
2671
2284
  },
@@ -2673,7 +2286,6 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
2673
2286
  tagged_vlans = DynamicModelMultipleChoiceField(
2674
2287
  queryset=VLAN.objects.all(),
2675
2288
  required=False,
2676
- brief_mode=False,
2677
2289
  query_params={"available_on_device": "$device"},
2678
2290
  )
2679
2291
  field_order = (
@@ -2765,7 +2377,6 @@ class InterfaceBulkEditForm(
2765
2377
  untagged_vlan = DynamicModelChoiceField(
2766
2378
  queryset=VLAN.objects.all(),
2767
2379
  required=False,
2768
- brief_mode=False,
2769
2380
  query_params={
2770
2381
  "location": "null",
2771
2382
  },
@@ -2773,7 +2384,6 @@ class InterfaceBulkEditForm(
2773
2384
  tagged_vlans = DynamicModelMultipleChoiceField(
2774
2385
  queryset=VLAN.objects.all(),
2775
2386
  required=False,
2776
- brief_mode=False,
2777
2387
  query_params={
2778
2388
  "location": "null",
2779
2389
  },
@@ -2846,67 +2456,6 @@ class InterfaceBulkEditForm(
2846
2456
  self.cleaned_data["tagged_vlans"] = []
2847
2457
 
2848
2458
 
2849
- class InterfaceCSVForm(CustomFieldModelCSVForm, StatusModelCSVFormMixin):
2850
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
2851
- parent_interface = CSVModelChoiceField(
2852
- queryset=Interface.objects.all(), required=False, to_field_name="name", help_text="Parent interface"
2853
- )
2854
- bridge = CSVModelChoiceField(
2855
- queryset=Interface.objects.all(), required=False, to_field_name="name", help_text="Bridge interface"
2856
- )
2857
- lag = CSVModelChoiceField(
2858
- queryset=Interface.objects.all(),
2859
- required=False,
2860
- to_field_name="name",
2861
- help_text="Parent LAG interface",
2862
- )
2863
- type = CSVChoiceField(choices=InterfaceTypeChoices, help_text="Physical medium")
2864
- mode = CSVChoiceField(
2865
- choices=InterfaceModeChoices,
2866
- required=False,
2867
- help_text="IEEE 802.1Q operational mode (for L2 interfaces)",
2868
- )
2869
-
2870
- def __init__(self, data=None, *args, **kwargs):
2871
- super().__init__(data, *args, **kwargs)
2872
-
2873
- if data:
2874
- # Limit choices for parent, bridge, and LAG interfaces to the assigned device (or VC)
2875
- device_name = data.get("device")
2876
- if device_name is not None:
2877
- device = Device.objects.filter(name=device_name).first()
2878
-
2879
- filter_by = Q(device=device)
2880
-
2881
- if device and device.virtual_chassis:
2882
- filter_by |= Q(device__virtual_chassis=device.virtual_chassis)
2883
-
2884
- self.fields["parent_interface"].queryset = (
2885
- self.fields["parent_interface"]
2886
- .queryset.filter(Q(filter_by))
2887
- .exclude(type__in=NONCONNECTABLE_IFACE_TYPES)
2888
- )
2889
- self.fields["bridge"].queryset = self.fields["bridge"].queryset.filter(filter_by)
2890
-
2891
- filter_by &= Q(type=InterfaceTypeChoices.TYPE_LAG)
2892
- self.fields["lag"].queryset = self.fields["lag"].queryset.filter(filter_by)
2893
- else:
2894
- self.fields["parent_interface"].queryset = self.fields["parent_interface"].queryset.none()
2895
- self.fields["bridge"].queryset = self.fields["bridge"].queryset.none()
2896
- self.fields["lag"].queryset = self.fields["lag"].queryset.none()
2897
-
2898
- class Meta:
2899
- model = Interface
2900
- fields = Interface.csv_headers
2901
-
2902
- def clean_enabled(self):
2903
- # Make sure enabled is True when it's not included in the uploaded data
2904
- if "enabled" not in self.data:
2905
- return True
2906
- else:
2907
- return self.cleaned_data["enabled"]
2908
-
2909
-
2910
2459
  #
2911
2460
  # Front pass-through ports
2912
2461
  #
@@ -3035,43 +2584,6 @@ class FrontPortBulkEditForm(
3035
2584
  nullable_fields = ["label", "description"]
3036
2585
 
3037
2586
 
3038
- class FrontPortCSVForm(CustomFieldModelCSVForm):
3039
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
3040
- rear_port = CSVModelChoiceField(
3041
- queryset=RearPort.objects.all(),
3042
- to_field_name="name",
3043
- help_text="Corresponding rear port",
3044
- )
3045
- type = CSVChoiceField(choices=PortTypeChoices, help_text="Physical medium classification")
3046
-
3047
- class Meta:
3048
- model = FrontPort
3049
- fields = FrontPort.csv_headers
3050
- help_texts = {
3051
- "rear_port_position": "Mapped position on corresponding rear port",
3052
- }
3053
-
3054
- def __init__(self, *args, **kwargs):
3055
- super().__init__(*args, **kwargs)
3056
-
3057
- # Limit RearPort choices to those belonging to this device (or VC master)
3058
- if self.is_bound:
3059
- try:
3060
- device = self.fields["device"].to_python(self.data["device"])
3061
- except forms.ValidationError:
3062
- device = None
3063
- else:
3064
- try:
3065
- device = self.instance.device
3066
- except Device.DoesNotExist:
3067
- device = None
3068
-
3069
- if device:
3070
- self.fields["rear_port"].queryset = RearPort.objects.filter(device__in=[device, device.get_vc_master()])
3071
- else:
3072
- self.fields["rear_port"].queryset = RearPort.objects.none()
3073
-
3074
-
3075
2587
  #
3076
2588
  # Rear pass-through ports
3077
2589
  #
@@ -3145,19 +2657,6 @@ class RearPortBulkEditForm(
3145
2657
  nullable_fields = ["label", "description"]
3146
2658
 
3147
2659
 
3148
- class RearPortCSVForm(CustomFieldModelCSVForm):
3149
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
3150
- type = CSVChoiceField(
3151
- help_text="Physical medium classification",
3152
- choices=PortTypeChoices,
3153
- )
3154
-
3155
- class Meta:
3156
- model = RearPort
3157
- fields = RearPort.csv_headers
3158
- help_texts = {"positions": "Number of front ports which may be mapped"}
3159
-
3160
-
3161
2660
  #
3162
2661
  # Device bays
3163
2662
  #
@@ -3222,49 +2721,6 @@ class DeviceBayBulkEditForm(
3222
2721
  nullable_fields = ["label", "description"]
3223
2722
 
3224
2723
 
3225
- class DeviceBayCSVForm(CustomFieldModelCSVForm):
3226
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
3227
- installed_device = CSVModelChoiceField(
3228
- queryset=Device.objects.all(),
3229
- required=False,
3230
- to_field_name="name",
3231
- help_text="Child device installed within this bay",
3232
- error_messages={
3233
- "invalid_choice": "Child device not found.",
3234
- },
3235
- )
3236
-
3237
- class Meta:
3238
- model = DeviceBay
3239
- fields = DeviceBay.csv_headers
3240
-
3241
- def __init__(self, *args, **kwargs):
3242
- super().__init__(*args, **kwargs)
3243
-
3244
- # Limit installed device choices to devices of the correct type and location
3245
- if self.is_bound:
3246
- try:
3247
- device = self.fields["device"].to_python(self.data["device"])
3248
- except forms.ValidationError:
3249
- device = None
3250
- else:
3251
- try:
3252
- device = self.instance.device
3253
- except Device.DoesNotExist:
3254
- device = None
3255
-
3256
- if device:
3257
- self.fields["installed_device"].queryset = Device.objects.filter(
3258
- location=device.location,
3259
- rack=device.rack,
3260
- parent_bay__isnull=True,
3261
- device_type__u_height=0,
3262
- device_type__subdevice_role=SubdeviceRoleChoices.ROLE_CHILD,
3263
- ).exclude(pk=device.pk)
3264
- else:
3265
- self.fields["installed_device"].queryset = Interface.objects.none()
3266
-
3267
-
3268
2724
  #
3269
2725
  # Inventory items
3270
2726
  #
@@ -3325,15 +2781,6 @@ class InventoryItemCreateForm(ComponentCreateForm):
3325
2781
  )
3326
2782
 
3327
2783
 
3328
- class InventoryItemCSVForm(CustomFieldModelCSVForm):
3329
- device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
3330
- manufacturer = CSVModelChoiceField(queryset=Manufacturer.objects.all(), to_field_name="name", required=False)
3331
-
3332
- class Meta:
3333
- model = InventoryItem
3334
- fields = InventoryItem.csv_headers
3335
-
3336
-
3337
2784
  class InventoryItemBulkCreateForm(
3338
2785
  form_from_model(InventoryItem, ["manufacturer", "part_id", "serial", "asset_tag", "discovered", "tags"]),
3339
2786
  DeviceBulkAddComponentForm,
@@ -3610,109 +3057,6 @@ class CableForm(NautobotModelForm):
3610
3057
  error_messages = {"length": {"max_value": "Maximum length is 32767 (any unit)"}}
3611
3058
 
3612
3059
 
3613
- class CableCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
3614
- # Termination A
3615
- side_a_device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Side A device")
3616
- side_a_type = CSVContentTypeField(
3617
- queryset=ContentType.objects.all(),
3618
- limit_choices_to=CABLE_TERMINATION_MODELS,
3619
- help_text="Side A type",
3620
- )
3621
- side_a_name = forms.CharField(help_text="Side A component name")
3622
-
3623
- # Termination B
3624
- side_b_device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Side B device")
3625
- side_b_type = CSVContentTypeField(
3626
- queryset=ContentType.objects.all(),
3627
- limit_choices_to=CABLE_TERMINATION_MODELS,
3628
- help_text="Side B type",
3629
- )
3630
- side_b_name = forms.CharField(help_text="Side B component name")
3631
-
3632
- # Cable attributes
3633
- type = CSVChoiceField(
3634
- choices=CableTypeChoices,
3635
- required=False,
3636
- help_text="Physical medium classification",
3637
- )
3638
- length_unit = CSVChoiceField(choices=CableLengthUnitChoices, required=False, help_text="Length unit")
3639
-
3640
- class Meta:
3641
- model = Cable
3642
- fields = [
3643
- "side_a_device",
3644
- "side_a_type",
3645
- "side_a_name",
3646
- "side_b_device",
3647
- "side_b_type",
3648
- "side_b_name",
3649
- "type",
3650
- "status",
3651
- "label",
3652
- "color",
3653
- "length",
3654
- "length_unit",
3655
- ]
3656
- help_texts = {
3657
- "color": mark_safe("RGB color in hexadecimal (e.g. <code>00ff00</code>)"),
3658
- "status": "Connection status",
3659
- }
3660
-
3661
- def _clean_side(self, side):
3662
- """
3663
- Derive a Cable's A/B termination objects.
3664
-
3665
- :param side: 'a' or 'b'
3666
- """
3667
- assert side in "ab", f"Invalid side designation: {side}"
3668
-
3669
- device = self.cleaned_data.get(f"side_{side}_device")
3670
- content_type = self.cleaned_data.get(f"side_{side}_type")
3671
- name = self.cleaned_data.get(f"side_{side}_name")
3672
- if not device or not content_type or not name:
3673
- return None
3674
-
3675
- model = content_type.model_class()
3676
- try:
3677
- termination_object = model.objects.get(device=device, name=name)
3678
- if termination_object.cable is not None:
3679
- raise forms.ValidationError(f"Side {side.upper()}: {device} {termination_object} is already connected")
3680
- except ObjectDoesNotExist:
3681
- raise forms.ValidationError(f"{side.upper()} side termination not found: {device} {name}")
3682
-
3683
- setattr(self.instance, f"termination_{side}", termination_object)
3684
- return termination_object
3685
-
3686
- def clean_side_a_name(self):
3687
- return self._clean_side("a")
3688
-
3689
- def clean_side_b_name(self):
3690
- return self._clean_side("b")
3691
-
3692
- def clean_length_unit(self):
3693
- # Avoid trying to save as NULL
3694
- length_unit = self.cleaned_data.get("length_unit", None)
3695
- return length_unit if length_unit is not None else ""
3696
-
3697
- def add_error(self, field, error):
3698
- # Edge Case: some fields in error are not properties in this instance
3699
- # e.g: termination_a_id not an property in CableCSVForm, This would raise a ValueError Exception
3700
- # Solution: convert those fields to its equivalent in CableCSVForm
3701
- # e.g: termination_a_id > side_a_name
3702
-
3703
- final_error = error
3704
- if hasattr(error, "error_dict"):
3705
- error_dict = error.error_dict
3706
- termination_keys = [key for key in error_dict.keys() if key.startswith("termination")]
3707
- for error_field in termination_keys:
3708
- side_value = error_field.split("_")[1]
3709
- error_msg = error_dict.pop(error_field)
3710
- error_dict[f"side_{side_value}_name"] = error_msg
3711
-
3712
- final_error = ValidationError(error_dict)
3713
- super().add_error(field, final_error)
3714
-
3715
-
3716
3060
  class CableBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, NautobotBulkEditForm):
3717
3061
  pk = forms.ModelMultipleChoiceField(queryset=Cable.objects.all(), widget=forms.MultipleHiddenInput)
3718
3062
  type = forms.ChoiceField(
@@ -3792,6 +3136,7 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
3792
3136
  queryset=Device.objects.all(),
3793
3137
  required=False,
3794
3138
  label="Device",
3139
+ to_field_name="name",
3795
3140
  query_params={"location": "$location"},
3796
3141
  )
3797
3142
 
@@ -3802,6 +3147,7 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
3802
3147
  queryset=Device.objects.all(),
3803
3148
  required=False,
3804
3149
  label="Device",
3150
+ to_field_name="name",
3805
3151
  query_params={"location": "$location"},
3806
3152
  )
3807
3153
 
@@ -3812,6 +3158,7 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
3812
3158
  queryset=Device.objects.all(),
3813
3159
  required=False,
3814
3160
  label="Device",
3161
+ to_field_name="name",
3815
3162
  query_params={"location": "$location"},
3816
3163
  )
3817
3164
 
@@ -3979,19 +3326,6 @@ class VirtualChassisBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
3979
3326
  nullable_fields = ["domain"]
3980
3327
 
3981
3328
 
3982
- class VirtualChassisCSVForm(CustomFieldModelCSVForm):
3983
- master = CSVModelChoiceField(
3984
- queryset=Device.objects.all(),
3985
- to_field_name="name",
3986
- required=False,
3987
- help_text="Master device",
3988
- )
3989
-
3990
- class Meta:
3991
- model = VirtualChassis
3992
- fields = VirtualChassis.csv_headers
3993
-
3994
-
3995
3329
  class VirtualChassisFilterForm(NautobotFilterForm):
3996
3330
  model = VirtualChassis
3997
3331
  q = forms.CharField(required=False, label="Search")
@@ -4034,22 +3368,6 @@ class PowerPanelForm(LocatableModelFormMixin, NautobotModelForm):
4034
3368
  ]
4035
3369
 
4036
3370
 
4037
- class PowerPanelCSVForm(LocatableModelCSVFormMixin, CustomFieldModelCSVForm):
4038
- rack_group = CSVModelChoiceField(queryset=RackGroup.objects.all(), required=False, to_field_name="name")
4039
-
4040
- class Meta:
4041
- model = PowerPanel
4042
- fields = PowerPanel.csv_headers
4043
-
4044
- def __init__(self, data=None, *args, **kwargs):
4045
- super().__init__(data, *args, **kwargs)
4046
-
4047
- if data:
4048
- # Limit rack_group queryset by assigned location
4049
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
4050
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
4051
-
4052
-
4053
3371
  class PowerPanelBulkEditForm(
4054
3372
  TagsBulkEditFormMixin,
4055
3373
  LocatableModelBulkEditFormMixin,
@@ -4123,53 +3441,6 @@ class PowerFeedForm(NautobotModelForm):
4123
3441
  }
4124
3442
 
4125
3443
 
4126
- class PowerFeedCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
4127
- location = CSVModelChoiceField(queryset=Location.objects.all(), to_field_name="name", help_text="Assigned location")
4128
- power_panel = CSVModelChoiceField(
4129
- queryset=PowerPanel.objects.all(),
4130
- to_field_name="name",
4131
- help_text="Upstream power panel",
4132
- )
4133
- rack_group = CSVModelChoiceField(
4134
- queryset=RackGroup.objects.all(),
4135
- to_field_name="name",
4136
- required=False,
4137
- help_text="Rack's group (if any)",
4138
- )
4139
- rack = CSVModelChoiceField(
4140
- queryset=Rack.objects.all(),
4141
- to_field_name="name",
4142
- required=False,
4143
- help_text="Rack",
4144
- )
4145
- type = CSVChoiceField(choices=PowerFeedTypeChoices, required=False, help_text="Primary or redundant")
4146
- supply = CSVChoiceField(choices=PowerFeedSupplyChoices, required=False, help_text="Supply type (AC/DC)")
4147
- phase = CSVChoiceField(choices=PowerFeedPhaseChoices, required=False, help_text="Single or three-phase")
4148
-
4149
- class Meta:
4150
- model = PowerFeed
4151
- fields = PowerFeed.csv_headers
4152
-
4153
- def __init__(self, data=None, *args, **kwargs):
4154
- super().__init__(data, *args, **kwargs)
4155
-
4156
- if data:
4157
- # Limit power_panel queryset by location
4158
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
4159
- self.fields["power_panel"].queryset = self.fields["power_panel"].queryset.filter(**params)
4160
-
4161
- # Limit rack_group queryset by location
4162
- params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
4163
- self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
4164
-
4165
- # Limit rack queryset by location and group
4166
- params = {
4167
- f"location__{self.fields['location'].to_field_name}": data.get("location"),
4168
- f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
4169
- }
4170
- self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
4171
-
4172
-
4173
3444
  class PowerFeedBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, NautobotBulkEditForm):
4174
3445
  pk = forms.ModelMultipleChoiceField(queryset=PowerFeed.objects.all(), widget=forms.MultipleHiddenInput)
4175
3446
  power_panel = DynamicModelChoiceField(queryset=PowerPanel.objects.all(), required=False)
@@ -4286,20 +3557,3 @@ class DeviceRedundancyGroupBulkEditForm(
4286
3557
  "failover_strategy",
4287
3558
  "secrets_group",
4288
3559
  ]
4289
-
4290
-
4291
- class DeviceRedundancyGroupCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
4292
- failover_strategy = CSVChoiceField(
4293
- choices=DeviceRedundancyGroupFailoverStrategyChoices, required=False, help_text="Failover Strategy"
4294
- )
4295
-
4296
- secrets_group = CSVModelChoiceField(
4297
- queryset=SecretsGroup.objects.all(),
4298
- required=False,
4299
- to_field_name="name",
4300
- help_text="Secrets group",
4301
- )
4302
-
4303
- class Meta:
4304
- model = DeviceRedundancyGroup
4305
- fields = DeviceRedundancyGroup.csv_headers