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
@@ -1,4 +1,5 @@
1
1
  import json
2
+ from unittest import skip
2
3
 
3
4
  from django.contrib.auth import get_user_model
4
5
  from django.contrib.contenttypes.models import ContentType
@@ -82,12 +83,20 @@ class Mixins:
82
83
  location=Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first(),
83
84
  device_type=DeviceType.objects.first(),
84
85
  role=Role.objects.get_for_model(Device).first(),
86
+ status=Status.objects.get_for_model(Device).first(),
85
87
  name="Peer Device",
86
88
  )
87
89
  if self.peer_termination_type is None:
88
90
  raise NotImplementedError("Test case must set peer_termination_type")
89
- peer_obj = self.peer_termination_type.objects.create(device=peer_device, name="Peer Termination")
90
- cable = Cable(termination_a=obj, termination_b=peer_obj, label="Cable 1")
91
+ if self.peer_termination_type is Interface:
92
+ intf_status = Status.objects.get_for_model(Interface).first()
93
+ peer_obj = self.peer_termination_type.objects.create(
94
+ device=peer_device, name="Peer Termination", status=intf_status
95
+ )
96
+ else:
97
+ peer_obj = self.peer_termination_type.objects.create(device=peer_device, name="Peer Termination")
98
+ cable_status = Status.objects.get_for_model(Cable).first()
99
+ cable = Cable(termination_a=obj, termination_b=peer_obj, label="Cable 1", status=cable_status)
91
100
  cable.save()
92
101
 
93
102
  self.add_permissions(f"dcim.view_{self.model._meta.model_name}")
@@ -105,7 +114,6 @@ class Mixins:
105
114
  """Mixin class for all `ComponentModel` model class tests."""
106
115
 
107
116
  model = None
108
- brief_fields = ["device", "display", "id", "name", "url"]
109
117
  bulk_update_data = {
110
118
  "description": "New description",
111
119
  }
@@ -118,25 +126,26 @@ class Mixins:
118
126
  cls.manufacturer = cls.device_type.manufacturer
119
127
  cls.location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
120
128
  cls.device_role = Role.objects.get_for_model(Device).first()
129
+ cls.device_status = Status.objects.get_for_model(Device).first()
121
130
  cls.device = Device.objects.create(
122
- device_type=cls.device_type, role=cls.device_role, name="Device 1", location=cls.location
131
+ device_type=cls.device_type,
132
+ role=cls.device_role,
133
+ name="Device 1",
134
+ location=cls.location,
135
+ status=cls.device_status,
123
136
  )
124
137
 
125
138
  class BasePortTestMixin(ComponentTraceMixin, BaseComponentTestMixin):
126
139
  """Mixin class for all `FooPort` tests."""
127
140
 
128
141
  peer_termination_type = None
129
- brief_fields = ["cable", "device", "display", "id", "name", "url"]
130
142
 
131
143
  class BasePortTemplateTestMixin(BaseComponentTestMixin):
132
144
  """Mixin class for all `FooPortTemplate` tests."""
133
145
 
134
- brief_fields = ["display", "id", "name", "url"]
135
-
136
146
 
137
- class LocationTypeTest(APIViewTestCases.APIViewTestCase):
147
+ class LocationTypeTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelAPIViewTestCaseMixin):
138
148
  model = LocationType
139
- brief_fields = ["display", "id", "name", "slug", "tree_depth", "url"]
140
149
  bulk_update_data = {
141
150
  "description": "Some generic description of multiple types. Not very useful.",
142
151
  "nestable": True,
@@ -177,9 +186,8 @@ class LocationTypeTest(APIViewTestCases.APIViewTestCase):
177
186
  ]
178
187
 
179
188
 
180
- class LocationTest(APIViewTestCases.APIViewTestCase):
189
+ class LocationTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelAPIViewTestCaseMixin):
181
190
  model = Location
182
- brief_fields = ["display", "id", "name", "slug", "tree_depth", "url"]
183
191
  choices_fields = []
184
192
  slug_source = ["parent__slug", "name"]
185
193
 
@@ -252,7 +260,6 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
252
260
  "status": self.location_statuses[0].pk,
253
261
  "time_zone": None,
254
262
  "location_type": self.lt1.pk,
255
- "location": self.loc1.pk,
256
263
  }
257
264
 
258
265
  # Attempt to create new location with null time_zone attr.
@@ -273,7 +280,6 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
273
280
  "status": self.location_statuses[0].pk,
274
281
  "time_zone": "",
275
282
  "location_type": self.lt1.pk,
276
- "location": self.loc1.pk,
277
283
  }
278
284
 
279
285
  # Attempt to create new location with blank time_zone attr.
@@ -295,7 +301,6 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
295
301
  "status": self.location_statuses[0].pk,
296
302
  "time_zone": time_zone,
297
303
  "location_type": self.lt1.pk,
298
- "location": self.loc1.pk,
299
304
  }
300
305
 
301
306
  # Attempt to create new location with valid time_zone attr.
@@ -317,7 +322,6 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
317
322
  "status": self.location_statuses[0].pk,
318
323
  "time_zone": time_zone,
319
324
  "location_type": self.lt1.pk,
320
- "location": self.loc1.pk,
321
325
  }
322
326
 
323
327
  # Attempt to create new location with invalid time_zone attr.
@@ -341,9 +345,8 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
341
345
  self.assertEqual(response.json()["time_zone"], None)
342
346
 
343
347
 
344
- class RackGroupTest(APIViewTestCases.APIViewTestCase):
348
+ class RackGroupTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelAPIViewTestCaseMixin):
345
349
  model = RackGroup
346
- brief_fields = ["display", "id", "name", "rack_count", "slug", "tree_depth", "url"]
347
350
  bulk_update_data = {
348
351
  "description": "New description",
349
352
  }
@@ -459,7 +462,6 @@ class RackGroupTest(APIViewTestCases.APIViewTestCase):
459
462
 
460
463
  class RackTest(APIViewTestCases.APIViewTestCase):
461
464
  model = Rack
462
- brief_fields = ["device_count", "display", "id", "name", "url"]
463
465
  choices_fields = ["outer_unit", "type", "width"]
464
466
 
465
467
  @classmethod
@@ -552,7 +554,7 @@ class RackTest(APIViewTestCases.APIViewTestCase):
552
554
  rack = Rack.objects.first()
553
555
  self.add_permissions("dcim.view_rack")
554
556
  url = reverse("dcim-api:rack-elevation", kwargs={"pk": rack.pk})
555
- params = {"brief": "true", "face": "front", "exclude": "a85a31aa-094f-4de9-8ba6-16cb088a1b74"}
557
+ params = {"face": "front", "exclude": "a85a31aa-094f-4de9-8ba6-16cb088a1b74"}
556
558
  response = self.client.get(url, params, **self.header)
557
559
  self.assertHttpStatus(response, 200)
558
560
 
@@ -604,7 +606,6 @@ class RackTest(APIViewTestCases.APIViewTestCase):
604
606
 
605
607
  class RackReservationTest(APIViewTestCases.APIViewTestCase):
606
608
  model = RackReservation
607
- brief_fields = ["display", "id", "units", "url", "user"]
608
609
  bulk_update_data = {
609
610
  "description": "New description",
610
611
  }
@@ -613,10 +614,11 @@ class RackReservationTest(APIViewTestCases.APIViewTestCase):
613
614
  def setUpTestData(cls):
614
615
  user = User.objects.create(username="user1", is_active=True)
615
616
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
617
+ rack_status = Status.objects.get_for_model(Rack).first()
616
618
 
617
619
  cls.racks = (
618
- Rack.objects.create(location=location, name="Rack 1"),
619
- Rack.objects.create(location=location, name="Rack 2"),
620
+ Rack.objects.create(location=location, name="Rack 1", status=rack_status),
621
+ Rack.objects.create(location=location, name="Rack 2", status=rack_status),
620
622
  )
621
623
 
622
624
  RackReservation.objects.create(rack=cls.racks[0], units=[1, 2, 3], user=user, description="Reservation #1")
@@ -651,7 +653,6 @@ class RackReservationTest(APIViewTestCases.APIViewTestCase):
651
653
 
652
654
  class ManufacturerTest(APIViewTestCases.APIViewTestCase):
653
655
  model = Manufacturer
654
- brief_fields = ["device_type_count", "display", "id", "name", "url"]
655
656
  create_data = [
656
657
  {
657
658
  "name": "Test Manufacturer 4",
@@ -681,15 +682,6 @@ class ManufacturerTest(APIViewTestCases.APIViewTestCase):
681
682
 
682
683
  class DeviceTypeTest(APIViewTestCases.APIViewTestCase):
683
684
  model = DeviceType
684
- brief_fields = [
685
- "device_count",
686
- "display",
687
- "id",
688
- "manufacturer",
689
- "model",
690
- "slug",
691
- "url",
692
- ]
693
685
  bulk_update_data = {
694
686
  "part_number": "ABC123",
695
687
  }
@@ -1019,7 +1011,6 @@ class DeviceBayTemplateTest(Mixins.BasePortTemplateTestMixin):
1019
1011
 
1020
1012
  class PlatformTest(APIViewTestCases.APIViewTestCase):
1021
1013
  model = Platform
1022
- brief_fields = ["device_count", "display", "id", "name", "url", "virtual_machine_count"]
1023
1014
  create_data = [
1024
1015
  {
1025
1016
  "name": "Test Platform 4",
@@ -1041,16 +1032,16 @@ class PlatformTest(APIViewTestCases.APIViewTestCase):
1041
1032
 
1042
1033
  class DeviceTest(APIViewTestCases.APIViewTestCase):
1043
1034
  model = Device
1044
- brief_fields = ["display", "id", "name", "url"]
1045
1035
  choices_fields = ["face"]
1046
1036
 
1047
1037
  @classmethod
1048
1038
  def setUpTestData(cls):
1049
1039
  locations = Location.objects.filter(location_type=LocationType.objects.get(name="Campus"))[:2]
1050
1040
 
1041
+ rack_status = Status.objects.get_for_model(Rack).first()
1051
1042
  racks = (
1052
- Rack.objects.create(name="Rack 1", location=locations[0]),
1053
- Rack.objects.create(name="Rack 2", location=locations[1]),
1043
+ Rack.objects.create(name="Rack 1", location=locations[0], status=rack_status),
1044
+ Rack.objects.create(name="Rack 2", location=locations[1], status=rack_status),
1054
1045
  )
1055
1046
 
1056
1047
  device_statuses = Status.objects.get_for_model(Device)
@@ -1140,27 +1131,28 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1140
1131
  "status": device_statuses[1].pk,
1141
1132
  }
1142
1133
 
1143
- def test_config_context_included_by_default_in_list_view(self):
1134
+ def test_config_context_excluded_by_default_in_list_view(self):
1144
1135
  """
1145
- Check that config context data is included by default in the devices list.
1136
+ Check that config context data is excluded by default in the devices list.
1146
1137
  """
1147
1138
  self.add_permissions("dcim.view_device")
1148
1139
  url = reverse("dcim-api:device-list")
1149
1140
  response = self.client.get(url, **self.header)
1150
1141
 
1151
1142
  self.assertHttpStatus(response, status.HTTP_200_OK)
1152
- self.assertEqual(response.data["results"][0].get("config_context", {}).get("A"), 1)
1143
+ self.assertNotIn("config_context", response.data["results"][0])
1153
1144
 
1154
- def test_config_context_excluded(self):
1145
+ def test_config_context_included(self):
1155
1146
  """
1156
- Check that config context data can be excluded by passing ?exclude=config_context.
1147
+ Check that config context data can be included by passing ?include=config_context.
1157
1148
  """
1158
1149
  self.add_permissions("dcim.view_device")
1159
- url = reverse("dcim-api:device-list") + "?exclude=config_context"
1150
+ url = reverse("dcim-api:device-list") + "?include=config_context"
1160
1151
  response = self.client.get(url, **self.header)
1161
1152
 
1162
1153
  self.assertHttpStatus(response, status.HTTP_200_OK)
1163
- self.assertFalse("config_context" in response.data["results"][0])
1154
+ self.assertIn("config_context", response.data["results"][0])
1155
+ self.assertEqual(response.data["results"][0]["config_context"], {"A": 1})
1164
1156
 
1165
1157
  def test_unique_name_per_location_constraint(self):
1166
1158
  """
@@ -1197,7 +1189,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1197
1189
  self._get_detail_url(Device.objects.get(name="Device 1")), patch_data, format="json", **self.header
1198
1190
  )
1199
1191
  self.assertHttpStatus(response, status.HTTP_200_OK)
1200
- self.assertEqual(response.data["local_config_context_schema"]["id"], str(schema.pk))
1192
+ self.assertEqual(str(response.data["local_config_context_schema"]), self.absolute_api_url(schema))
1201
1193
 
1202
1194
  def test_local_config_context_schema_schema_validation_fails(self):
1203
1195
  """
@@ -1226,10 +1218,13 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1226
1218
  self.add_permissions("dcim.change_device")
1227
1219
 
1228
1220
  dev = Device.objects.get(name="Device 3")
1229
- dev_intf = Interface.objects.create(name="Ethernet1", device=dev, type="1000base-t")
1221
+ intf_status = Status.objects.get_for_model(Interface).first()
1222
+ dev_intf = Interface.objects.create(name="Ethernet1", device=dev, type="1000base-t", status=intf_status)
1223
+ ipaddr_status = Status.objects.get_for_model(IPAddress).first()
1224
+ prefix_status = Status.objects.get_for_model(Prefix).first()
1230
1225
  namespace = Namespace.objects.first()
1231
- Prefix.objects.create(prefix="192.0.2.0/24", namespace=namespace)
1232
- dev_ip_addr = IPAddress.objects.create(address="192.0.2.1/24", namespace=namespace)
1226
+ Prefix.objects.create(prefix="192.0.2.0/24", namespace=namespace, status=prefix_status)
1227
+ dev_ip_addr = IPAddress.objects.create(address="192.0.2.1/24", namespace=namespace, status=ipaddr_status)
1233
1228
  dev_intf.add_ip_addresses(dev_ip_addr)
1234
1229
 
1235
1230
  patch_data = {"primary_ip4": dev_ip_addr.pk}
@@ -1408,14 +1403,20 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1408
1403
  interface_status = Status.objects.get_for_model(Interface).first()
1409
1404
 
1410
1405
  cls.devices = (
1406
+ cls.device,
1411
1407
  Device.objects.create(
1412
- device_type=cls.device_type, role=cls.device_role, name="Device 1", location=cls.location
1413
- ),
1414
- Device.objects.create(
1415
- device_type=cls.device_type, role=cls.device_role, name="Device 2", location=cls.location
1408
+ device_type=cls.device_type,
1409
+ role=cls.device_role,
1410
+ status=cls.device_status,
1411
+ name="Device 2",
1412
+ location=cls.location,
1416
1413
  ),
1417
1414
  Device.objects.create(
1418
- device_type=cls.device_type, role=cls.device_role, name="Device 3", location=cls.location
1415
+ device_type=cls.device_type,
1416
+ role=cls.device_role,
1417
+ status=cls.device_status,
1418
+ name="Device 3",
1419
+ location=cls.location,
1419
1420
  ),
1420
1421
  )
1421
1422
 
@@ -1425,24 +1426,58 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1425
1426
  Device.objects.filter(id=cls.devices[0].id).update(virtual_chassis=cls.virtual_chassis, vc_position=1)
1426
1427
  Device.objects.filter(id=cls.devices[1].id).update(virtual_chassis=cls.virtual_chassis, vc_position=2)
1427
1428
 
1429
+ # Interfaces have special handling around the "Active" status so let's set our interfaces to something else.
1430
+ non_default_status = Status.objects.get_for_model(Interface).exclude(name="Active").first()
1428
1431
  cls.interfaces = (
1429
- Interface.objects.create(device=cls.devices[0], name="Interface 1", type="1000base-t"),
1430
- Interface.objects.create(device=cls.devices[0], name="Interface 2", type="1000base-t"),
1431
- Interface.objects.create(device=cls.devices[0], name="Interface 3", type=InterfaceTypeChoices.TYPE_BRIDGE),
1432
1432
  Interface.objects.create(
1433
- device=cls.devices[1], name="Interface 4", type=InterfaceTypeChoices.TYPE_1GE_GBIC
1433
+ device=cls.devices[0],
1434
+ name="Interface 1",
1435
+ type="1000base-t",
1436
+ status=non_default_status,
1437
+ ),
1438
+ Interface.objects.create(
1439
+ device=cls.devices[0],
1440
+ name="Interface 2",
1441
+ type="1000base-t",
1442
+ status=non_default_status,
1443
+ ),
1444
+ Interface.objects.create(
1445
+ device=cls.devices[0],
1446
+ name="Interface 3",
1447
+ type=InterfaceTypeChoices.TYPE_BRIDGE,
1448
+ status=non_default_status,
1449
+ ),
1450
+ Interface.objects.create(
1451
+ device=cls.devices[1],
1452
+ name="Interface 4",
1453
+ type=InterfaceTypeChoices.TYPE_1GE_GBIC,
1454
+ status=non_default_status,
1434
1455
  ),
1435
- Interface.objects.create(device=cls.devices[1], name="Interface 5", type=InterfaceTypeChoices.TYPE_LAG),
1436
- Interface.objects.create(device=cls.devices[2], name="Interface 6", type=InterfaceTypeChoices.TYPE_LAG),
1437
1456
  Interface.objects.create(
1438
- device=cls.devices[2], name="Interface 7", type=InterfaceTypeChoices.TYPE_1GE_GBIC
1457
+ device=cls.devices[1],
1458
+ name="Interface 5",
1459
+ type=InterfaceTypeChoices.TYPE_LAG,
1460
+ status=non_default_status,
1461
+ ),
1462
+ Interface.objects.create(
1463
+ device=cls.devices[2],
1464
+ name="Interface 6",
1465
+ type=InterfaceTypeChoices.TYPE_LAG,
1466
+ status=non_default_status,
1467
+ ),
1468
+ Interface.objects.create(
1469
+ device=cls.devices[2],
1470
+ name="Interface 7",
1471
+ type=InterfaceTypeChoices.TYPE_1GE_GBIC,
1472
+ status=non_default_status,
1439
1473
  ),
1440
1474
  )
1441
1475
 
1476
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1442
1477
  cls.vlans = (
1443
- VLAN.objects.create(name="VLAN 1", vid=1),
1444
- VLAN.objects.create(name="VLAN 2", vid=2),
1445
- VLAN.objects.create(name="VLAN 3", vid=3),
1478
+ VLAN.objects.create(name="VLAN 1", vid=1, status=vlan_status),
1479
+ VLAN.objects.create(name="VLAN 2", vid=2, status=vlan_status),
1480
+ VLAN.objects.create(name="VLAN 3", vid=3, status=vlan_status),
1446
1481
  )
1447
1482
 
1448
1483
  cls.create_data = [
@@ -1454,6 +1489,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1454
1489
  "mode": InterfaceModeChoices.MODE_TAGGED,
1455
1490
  "tagged_vlans": [cls.vlans[0].pk, cls.vlans[1].pk],
1456
1491
  "untagged_vlan": cls.vlans[2].pk,
1492
+ "mac_address": "00-01-02-03-04-05",
1457
1493
  },
1458
1494
  {
1459
1495
  "device": cls.devices[0].pk,
@@ -1620,6 +1656,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1620
1656
  name="Tagged Interface",
1621
1657
  mode=InterfaceModeChoices.MODE_TAGGED,
1622
1658
  type=InterfaceTypeChoices.TYPE_VIRTUAL,
1659
+ status=Status.objects.get_for_model(Interface).first(),
1623
1660
  )
1624
1661
  interface.tagged_vlans.add(self.vlans[0])
1625
1662
  payload = {"mode": None, "tagged_vlans": [self.vlans[2].pk]}
@@ -1760,28 +1797,33 @@ class DeviceBayTest(Mixins.BaseComponentTestMixin):
1760
1797
  )
1761
1798
 
1762
1799
  devices = (
1800
+ # "Device 1" was already created in super().setUpTestData
1763
1801
  Device.objects.create(
1764
1802
  device_type=device_types[0],
1765
1803
  role=cls.device_role,
1766
- name="Device 1",
1804
+ status=cls.device_status,
1805
+ name="Device 2",
1767
1806
  location=cls.location,
1768
1807
  ),
1769
1808
  Device.objects.create(
1770
1809
  device_type=device_types[1],
1771
1810
  role=cls.device_role,
1772
- name="Device 2",
1811
+ status=cls.device_status,
1812
+ name="Device 3",
1773
1813
  location=cls.location,
1774
1814
  ),
1775
1815
  Device.objects.create(
1776
1816
  device_type=device_types[1],
1777
1817
  role=cls.device_role,
1778
- name="Device 3",
1818
+ status=cls.device_status,
1819
+ name="Device 4",
1779
1820
  location=cls.location,
1780
1821
  ),
1781
1822
  Device.objects.create(
1782
1823
  device_type=device_types[1],
1783
1824
  role=cls.device_role,
1784
- name="Device 4",
1825
+ status=cls.device_status,
1826
+ name="Device 5",
1785
1827
  location=cls.location,
1786
1828
  ),
1787
1829
  )
@@ -1809,9 +1851,8 @@ class DeviceBayTest(Mixins.BaseComponentTestMixin):
1809
1851
  ]
1810
1852
 
1811
1853
 
1812
- class InventoryItemTest(Mixins.BaseComponentTestMixin):
1854
+ class InventoryItemTest(Mixins.BaseComponentTestMixin, APIViewTestCases.TreeModelAPIViewTestCaseMixin):
1813
1855
  model = InventoryItem
1814
- brief_fields = ["device", "display", "id", "name", "tree_depth", "url"]
1815
1856
  choices_fields = []
1816
1857
 
1817
1858
  @classmethod
@@ -1840,10 +1881,18 @@ class InventoryItemTest(Mixins.BaseComponentTestMixin):
1840
1881
  },
1841
1882
  ]
1842
1883
 
1884
+ # TODO: Unskip after resolving #2908, #2909
1885
+ @skip("DRF's built-in InventoryItem nautral_key is infinitely recursive")
1886
+ def test_list_objects_ascending_ordered(self):
1887
+ pass
1888
+
1889
+ @skip("DRF's built-in InventoryItem nautral_key is infinitely recursive")
1890
+ def test_list_objects_descending_ordered(self):
1891
+ pass
1892
+
1843
1893
 
1844
1894
  class CableTest(Mixins.BaseComponentTestMixin):
1845
1895
  model = Cable
1846
- brief_fields = ["display", "id", "label", "url"]
1847
1896
  bulk_update_data = {
1848
1897
  "length": 100,
1849
1898
  "length_unit": "m",
@@ -1860,18 +1909,21 @@ class CableTest(Mixins.BaseComponentTestMixin):
1860
1909
  Device.objects.create(
1861
1910
  device_type=cls.device_type,
1862
1911
  role=cls.device_role,
1912
+ status=cls.device_status,
1863
1913
  name="Device 2",
1864
1914
  location=cls.location,
1865
1915
  ),
1866
1916
  Device.objects.create(
1867
1917
  device_type=cls.device_type,
1868
1918
  role=cls.device_role,
1919
+ status=cls.device_status,
1869
1920
  name="Device 3",
1870
1921
  location=cls.location,
1871
1922
  ),
1872
1923
  )
1873
1924
 
1874
1925
  interfaces = []
1926
+ interface_status = Status.objects.get_for_model(Interface).first()
1875
1927
  for device in devices:
1876
1928
  for i in range(0, 10):
1877
1929
  interfaces.append(
@@ -1879,6 +1931,7 @@ class CableTest(Mixins.BaseComponentTestMixin):
1879
1931
  device=device,
1880
1932
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
1881
1933
  name=f"eth{i}",
1934
+ status=interface_status,
1882
1935
  )
1883
1936
  )
1884
1937
 
@@ -1938,23 +1991,27 @@ class ConnectedDeviceTest(APITestCase):
1938
1991
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1939
1992
  device_type = DeviceType.objects.exclude(manufacturer__isnull=True).first()
1940
1993
  device_role = Role.objects.get_for_model(Device).first()
1994
+ device_status = Status.objects.get_for_model(Device).first()
1941
1995
 
1942
1996
  cable_status = Status.objects.get_for_model(Cable).get(name="Connected")
1943
1997
 
1944
1998
  self.device1 = Device.objects.create(
1945
1999
  device_type=device_type,
1946
2000
  role=device_role,
2001
+ status=device_status,
1947
2002
  name="TestDevice1",
1948
2003
  location=location,
1949
2004
  )
1950
2005
  device2 = Device.objects.create(
1951
2006
  device_type=device_type,
1952
2007
  role=device_role,
2008
+ status=device_status,
1953
2009
  name="TestDevice2",
1954
2010
  location=location,
1955
2011
  )
1956
- interface1 = Interface.objects.create(device=self.device1, name="eth0")
1957
- interface2 = Interface.objects.create(device=device2, name="eth0")
2012
+ interface_status = Status.objects.get_for_model(Interface).first()
2013
+ interface1 = Interface.objects.create(device=self.device1, name="eth0", status=interface_status)
2014
+ interface2 = Interface.objects.create(device=device2, name="eth0", status=interface_status)
1958
2015
 
1959
2016
  cable = Cable(termination_a=interface1, termination_b=interface2, status=cable_status)
1960
2017
  cable.validated_save()
@@ -1969,90 +2026,103 @@ class ConnectedDeviceTest(APITestCase):
1969
2026
 
1970
2027
  class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
1971
2028
  model = VirtualChassis
1972
- brief_fields = ["display", "id", "master", "member_count", "name", "url"]
1973
2029
 
1974
2030
  @classmethod
1975
2031
  def setUpTestData(cls):
1976
2032
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1977
2033
  device_type = DeviceType.objects.exclude(manufacturer__isnull=True).first()
1978
2034
  device_role = Role.objects.get_for_model(Device).first()
2035
+ device_status = Status.objects.get_for_model(Device).first()
1979
2036
 
1980
2037
  devices = (
1981
2038
  Device.objects.create(
1982
2039
  name="Device 1",
1983
2040
  device_type=device_type,
1984
2041
  role=device_role,
2042
+ status=device_status,
1985
2043
  location=location,
1986
2044
  ),
1987
2045
  Device.objects.create(
1988
2046
  name="Device 2",
1989
2047
  device_type=device_type,
1990
2048
  role=device_role,
2049
+ status=device_status,
1991
2050
  location=location,
1992
2051
  ),
1993
2052
  Device.objects.create(
1994
2053
  name="Device 3",
1995
2054
  device_type=device_type,
1996
2055
  role=device_role,
2056
+ status=device_status,
1997
2057
  location=location,
1998
2058
  ),
1999
2059
  Device.objects.create(
2000
2060
  name="Device 4",
2001
2061
  device_type=device_type,
2002
2062
  role=device_role,
2063
+ status=device_status,
2003
2064
  location=location,
2004
2065
  ),
2005
2066
  Device.objects.create(
2006
2067
  name="Device 5",
2007
2068
  device_type=device_type,
2008
2069
  role=device_role,
2070
+ status=device_status,
2009
2071
  location=location,
2010
2072
  ),
2011
2073
  Device.objects.create(
2012
2074
  name="Device 6",
2013
2075
  device_type=device_type,
2014
2076
  role=device_role,
2077
+ status=device_status,
2015
2078
  location=location,
2016
2079
  ),
2017
2080
  Device.objects.create(
2018
2081
  name="Device 7",
2019
2082
  device_type=device_type,
2020
2083
  role=device_role,
2084
+ status=device_status,
2021
2085
  location=location,
2022
2086
  ),
2023
2087
  Device.objects.create(
2024
2088
  name="Device 8",
2025
2089
  device_type=device_type,
2026
2090
  role=device_role,
2091
+ status=device_status,
2027
2092
  location=location,
2028
2093
  ),
2029
2094
  Device.objects.create(
2030
2095
  name="Device 9",
2031
2096
  device_type=device_type,
2032
2097
  role=device_role,
2098
+ status=device_status,
2033
2099
  location=location,
2034
2100
  ),
2035
2101
  Device.objects.create(
2036
2102
  name="Device 10",
2037
2103
  device_type=device_type,
2038
2104
  role=device_role,
2105
+ status=device_status,
2039
2106
  location=location,
2040
2107
  ),
2041
2108
  Device.objects.create(
2042
2109
  name="Device 11",
2043
2110
  device_type=device_type,
2044
2111
  role=device_role,
2112
+ status=device_status,
2045
2113
  location=location,
2046
2114
  ),
2047
2115
  Device.objects.create(
2048
2116
  name="Device 12",
2049
2117
  device_type=device_type,
2050
2118
  role=device_role,
2119
+ status=device_status,
2051
2120
  location=location,
2052
2121
  ),
2053
2122
  )
2054
2123
 
2055
2124
  # Create 12 interfaces per device
2125
+ interface_status = Status.objects.get_for_model(Interface).first()
2056
2126
  interfaces = []
2057
2127
  for i, device in enumerate(devices):
2058
2128
  for j in range(0, 13):
@@ -2062,6 +2132,7 @@ class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
2062
2132
  device=device,
2063
2133
  name=f"{i%3+1}/{j}",
2064
2134
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2135
+ status=interface_status,
2065
2136
  )
2066
2137
  )
2067
2138
 
@@ -2137,9 +2208,10 @@ class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
2137
2208
  virtual_chassis_1 = response.json()["results"][0]
2138
2209
 
2139
2210
  # Make sure the master is set
2140
- self.assertNotEqual(virtual_chassis_1["master"], None)
2211
+ self.assertIsNotNone(virtual_chassis_1["master"])
2141
2212
 
2142
- master_device = Device.objects.get(pk=virtual_chassis_1["master"]["id"])
2213
+ # The `master` key will be a URL now, but it contains the PK
2214
+ master_device = Device.objects.get(pk=virtual_chassis_1["master"].split("/")[-2])
2143
2215
 
2144
2216
  # Set the virtual_chassis of the master device to null
2145
2217
  url = reverse("dcim-api:device-detail", kwargs={"pk": master_device.id})
@@ -2159,7 +2231,6 @@ class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
2159
2231
 
2160
2232
  class PowerPanelTest(APIViewTestCases.APIViewTestCase):
2161
2233
  model = PowerPanel
2162
- brief_fields = ["display", "id", "name", "power_feed_count", "url"]
2163
2234
 
2164
2235
  @classmethod
2165
2236
  def setUpTestData(cls):
@@ -2198,7 +2269,6 @@ class PowerPanelTest(APIViewTestCases.APIViewTestCase):
2198
2269
 
2199
2270
  class PowerFeedTest(APIViewTestCases.APIViewTestCase):
2200
2271
  model = PowerFeed
2201
- brief_fields = ["cable", "display", "id", "name", "url"]
2202
2272
  choices_fields = ["phase", "supply", "type"]
2203
2273
 
2204
2274
  @classmethod
@@ -2206,12 +2276,21 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
2206
2276
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
2207
2277
  rackgroup = RackGroup.objects.create(location=location, name="Rack Group 1", slug="rack-group-1")
2208
2278
  rackrole = Role.objects.get_for_model(Rack).first()
2279
+ rackstatus = Status.objects.get_for_model(Rack).first()
2209
2280
 
2210
2281
  racks = (
2211
- Rack.objects.create(location=location, rack_group=rackgroup, role=rackrole, name="Rack 1"),
2212
- Rack.objects.create(location=location, rack_group=rackgroup, role=rackrole, name="Rack 2"),
2213
- Rack.objects.create(location=location, rack_group=rackgroup, role=rackrole, name="Rack 3"),
2214
- Rack.objects.create(location=location, rack_group=rackgroup, role=rackrole, name="Rack 4"),
2282
+ Rack.objects.create(
2283
+ location=location, rack_group=rackgroup, role=rackrole, name="Rack 1", status=rackstatus
2284
+ ),
2285
+ Rack.objects.create(
2286
+ location=location, rack_group=rackgroup, role=rackrole, name="Rack 2", status=rackstatus
2287
+ ),
2288
+ Rack.objects.create(
2289
+ location=location, rack_group=rackgroup, role=rackrole, name="Rack 3", status=rackstatus
2290
+ ),
2291
+ Rack.objects.create(
2292
+ location=location, rack_group=rackgroup, role=rackrole, name="Rack 4", status=rackstatus
2293
+ ),
2215
2294
  )
2216
2295
 
2217
2296
  power_panels = (
@@ -2221,40 +2300,47 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
2221
2300
 
2222
2301
  PRIMARY = PowerFeedTypeChoices.TYPE_PRIMARY
2223
2302
  REDUNDANT = PowerFeedTypeChoices.TYPE_REDUNDANT
2303
+ pf_status = Status.objects.get_for_model(PowerFeed).first()
2224
2304
  PowerFeed.objects.create(
2225
2305
  power_panel=power_panels[0],
2226
2306
  rack=racks[0],
2227
2307
  name="Power Feed 1A",
2308
+ status=pf_status,
2228
2309
  type=PRIMARY,
2229
2310
  )
2230
2311
  PowerFeed.objects.create(
2231
2312
  power_panel=power_panels[1],
2232
2313
  rack=racks[0],
2233
2314
  name="Power Feed 1B",
2315
+ status=pf_status,
2234
2316
  type=REDUNDANT,
2235
2317
  )
2236
2318
  PowerFeed.objects.create(
2237
2319
  power_panel=power_panels[0],
2238
2320
  rack=racks[1],
2239
2321
  name="Power Feed 2A",
2322
+ status=pf_status,
2240
2323
  type=PRIMARY,
2241
2324
  )
2242
2325
  PowerFeed.objects.create(
2243
2326
  power_panel=power_panels[1],
2244
2327
  rack=racks[1],
2245
2328
  name="Power Feed 2B",
2329
+ status=pf_status,
2246
2330
  type=REDUNDANT,
2247
2331
  )
2248
2332
  PowerFeed.objects.create(
2249
2333
  power_panel=power_panels[0],
2250
2334
  rack=racks[2],
2251
2335
  name="Power Feed 3A",
2336
+ status=pf_status,
2252
2337
  type=PRIMARY,
2253
2338
  )
2254
2339
  PowerFeed.objects.create(
2255
2340
  power_panel=power_panels[1],
2256
2341
  rack=racks[2],
2257
2342
  name="Power Feed 3B",
2343
+ status=pf_status,
2258
2344
  type=REDUNDANT,
2259
2345
  )
2260
2346
 
@@ -2283,7 +2369,6 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
2283
2369
 
2284
2370
  class DeviceRedundancyGroupTest(APIViewTestCases.APIViewTestCase):
2285
2371
  model = DeviceRedundancyGroup
2286
- brief_fields = ["display", "failover_strategy", "id", "name", "url"]
2287
2372
  choices_fields = ["failover_strategy"]
2288
2373
 
2289
2374
  @classmethod