nautobot 2.0.0a2__py3-none-any.whl → 2.0.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1029) hide show
  1. nautobot/__init__.py +1 -5
  2. nautobot/apps/api.py +6 -8
  3. nautobot/apps/forms.py +0 -2
  4. nautobot/apps/ui.py +0 -8
  5. nautobot/circuits/api/serializers.py +9 -119
  6. nautobot/circuits/api/urls.py +1 -1
  7. nautobot/circuits/api/views.py +0 -1
  8. nautobot/circuits/choices.py +0 -2
  9. nautobot/circuits/filters.py +7 -6
  10. nautobot/circuits/forms.py +3 -73
  11. nautobot/circuits/migrations/0001_initial_part_1.py +0 -1
  12. nautobot/circuits/migrations/0002_initial_part_2.py +0 -1
  13. nautobot/circuits/migrations/0003_auto_slug.py +0 -1
  14. nautobot/circuits/migrations/0004_increase_provider_account_length.py +0 -1
  15. nautobot/circuits/migrations/0005_providernetwork.py +0 -1
  16. nautobot/circuits/migrations/0006_cache_circuit_terminations.py +0 -1
  17. nautobot/circuits/migrations/0007_circuitterminations_primary_model.py +0 -1
  18. nautobot/circuits/migrations/0008_add_natural_indexing.py +0 -1
  19. nautobot/circuits/migrations/0009_circuittermination_location.py +0 -1
  20. nautobot/circuits/migrations/0010_rename_foreign_keys_and_related_names.py +0 -1
  21. nautobot/circuits/migrations/0011_remove_site_foreign_key_from_circuit_termination_class.py +0 -1
  22. nautobot/circuits/migrations/0012_created_datetime.py +0 -1
  23. nautobot/circuits/migrations/0013_alter_circuittermination__path.py +0 -1
  24. nautobot/circuits/migrations/0014_related_name_changes.py +1 -2
  25. nautobot/circuits/migrations/0015_remove_circuittype_provider_slug.py +20 -0
  26. nautobot/circuits/migrations/0016_tagsfield.py +34 -0
  27. nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
  28. nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
  29. nautobot/circuits/models.py +3 -93
  30. nautobot/circuits/navigation.py +14 -69
  31. nautobot/circuits/signals.py +0 -2
  32. nautobot/circuits/tables.py +42 -5
  33. nautobot/circuits/templates/circuits/circuit_retrieve.html +1 -1
  34. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -1
  35. nautobot/circuits/templates/circuits/circuittype_retrieve.html +1 -1
  36. nautobot/circuits/templates/circuits/provider_create.html +0 -1
  37. nautobot/circuits/templates/circuits/provider_retrieve.html +1 -1
  38. nautobot/circuits/tests/integration/test_relationships.py +13 -16
  39. nautobot/circuits/tests/test_api.py +13 -43
  40. nautobot/circuits/tests/test_filters.py +20 -15
  41. nautobot/circuits/tests/test_models.py +7 -3
  42. nautobot/circuits/tests/test_views.py +57 -67
  43. nautobot/circuits/views.py +18 -9
  44. nautobot/core/api/__init__.py +8 -2
  45. nautobot/core/api/authentication.py +0 -3
  46. nautobot/core/api/fields.py +15 -6
  47. nautobot/core/api/filter_backends.py +3 -2
  48. nautobot/core/api/metadata.py +237 -30
  49. nautobot/core/api/mixins.py +94 -0
  50. nautobot/core/api/pagination.py +3 -3
  51. nautobot/core/api/parsers.py +154 -0
  52. nautobot/core/api/renderers.py +153 -2
  53. nautobot/core/api/schema.py +47 -3
  54. nautobot/core/api/serializers.py +377 -37
  55. nautobot/core/api/urls.py +11 -3
  56. nautobot/core/api/utils.py +174 -2
  57. nautobot/core/api/versioning.py +32 -10
  58. nautobot/core/api/views.py +266 -75
  59. nautobot/core/apps/__init__.py +138 -221
  60. nautobot/core/celery/__init__.py +112 -41
  61. nautobot/core/celery/backends.py +19 -13
  62. nautobot/core/celery/control.py +46 -0
  63. nautobot/core/celery/encoders.py +53 -0
  64. nautobot/core/celery/log.py +38 -0
  65. nautobot/core/celery/schedulers.py +23 -4
  66. nautobot/core/celery/task.py +1 -16
  67. nautobot/core/checks.py +0 -27
  68. nautobot/core/choices.py +21 -113
  69. nautobot/core/{cli.py → cli/__init__.py} +1 -2
  70. nautobot/core/cli/__main__.py +3 -0
  71. nautobot/core/constants.py +25 -43
  72. nautobot/core/context_processors.py +12 -0
  73. nautobot/core/filters.py +2 -2
  74. nautobot/core/forms/__init__.py +0 -4
  75. nautobot/core/forms/fields.py +39 -68
  76. nautobot/core/forms/forms.py +27 -27
  77. nautobot/core/forms/utils.py +7 -59
  78. nautobot/core/forms/widgets.py +0 -1
  79. nautobot/core/graphql/__init__.py +2 -2
  80. nautobot/core/graphql/schema.py +4 -27
  81. nautobot/core/jobs/__init__.py +75 -0
  82. nautobot/core/management/commands/build_ui.py +255 -0
  83. nautobot/core/management/commands/celery.py +0 -1
  84. nautobot/core/management/commands/generate_test_data.py +18 -13
  85. nautobot/core/management/commands/post_upgrade.py +24 -24
  86. nautobot/core/management/commands/validate_models.py +0 -1
  87. nautobot/core/middleware.py +0 -1
  88. nautobot/core/models/__init__.py +26 -1
  89. nautobot/core/models/fields.py +24 -5
  90. nautobot/core/models/generics.py +2 -46
  91. nautobot/core/models/managers.py +5 -0
  92. nautobot/core/models/name_color_content_types.py +1 -19
  93. nautobot/core/models/tree_queries.py +14 -4
  94. nautobot/core/models/utils.py +9 -10
  95. nautobot/core/models/validators.py +17 -8
  96. nautobot/core/releases.py +8 -10
  97. nautobot/core/settings.py +81 -53
  98. nautobot/core/tables.py +5 -5
  99. nautobot/core/tasks.py +4 -7
  100. nautobot/core/templates/base.html +1 -49
  101. nautobot/core/templates/base_django.html +49 -0
  102. nautobot/core/templates/base_react.html +55 -0
  103. nautobot/core/templates/buttons/export.html +6 -4
  104. nautobot/core/templates/generic/object_bulk_create.html +10 -21
  105. nautobot/core/templates/generic/object_list.html +4 -1
  106. nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
  107. nautobot/core/templates/inc/footer.html +1 -0
  108. nautobot/core/templates/inc/javascript.html +0 -14
  109. nautobot/core/templates/inc/nav_menu.html +28 -33
  110. nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
  111. nautobot/core/templates/inc/relationships_table_rows.html +2 -2
  112. nautobot/core/templates/nautobot_config.py.j2 +8 -25
  113. nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
  114. nautobot/core/templates/rest_framework/api.html +8 -0
  115. nautobot/core/templatetags/buttons.py +32 -29
  116. nautobot/core/templatetags/helpers.py +1 -1
  117. nautobot/core/testing/__init__.py +47 -44
  118. nautobot/core/testing/api.py +365 -47
  119. nautobot/core/testing/filters.py +12 -7
  120. nautobot/core/testing/integration.py +1 -1
  121. nautobot/core/testing/migrations.py +2 -0
  122. nautobot/core/testing/mixins.py +22 -12
  123. nautobot/core/testing/schema.py +2 -1
  124. nautobot/core/testing/views.py +28 -51
  125. nautobot/core/tests/integration/test_filters.py +17 -8
  126. nautobot/core/tests/integration/test_navbar.py +11 -34
  127. nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
  128. nautobot/core/tests/nautobot_config.py +2 -3
  129. nautobot/core/tests/runner.py +0 -1
  130. nautobot/core/tests/test_api.py +290 -24
  131. nautobot/core/tests/test_authentication.py +57 -14
  132. nautobot/core/tests/test_checks.py +0 -7
  133. nautobot/core/tests/test_choices.py +0 -1
  134. nautobot/core/tests/test_filters.py +117 -110
  135. nautobot/core/tests/test_forms.py +47 -110
  136. nautobot/core/tests/test_graphql.py +158 -135
  137. nautobot/core/tests/test_logging.py +4 -1
  138. nautobot/core/tests/test_managers.py +3 -5
  139. nautobot/core/tests/test_models.py +2 -0
  140. nautobot/core/tests/test_ordering.py +0 -2
  141. nautobot/core/tests/test_paginator.py +3 -1
  142. nautobot/core/tests/test_releases.py +12 -12
  143. nautobot/core/tests/test_templatetags_helpers.py +7 -4
  144. nautobot/core/tests/test_utils.py +112 -78
  145. nautobot/core/tests/test_views.py +12 -17
  146. nautobot/core/tests/test_views_utils.py +6 -9
  147. nautobot/core/utils/data.py +17 -0
  148. nautobot/core/utils/deprecation.py +13 -20
  149. nautobot/core/utils/filtering.py +53 -9
  150. nautobot/core/utils/git.py +12 -4
  151. nautobot/core/utils/lookup.py +3 -1
  152. nautobot/core/utils/requests.py +23 -116
  153. nautobot/core/views/__init__.py +1 -2
  154. nautobot/core/views/generic.py +131 -119
  155. nautobot/core/views/mixins.py +53 -62
  156. nautobot/core/views/paginator.py +0 -1
  157. nautobot/core/views/renderers.py +14 -12
  158. nautobot/core/views/utils.py +87 -4
  159. nautobot/dcim/api/serializers.py +160 -672
  160. nautobot/dcim/api/urls.py +1 -1
  161. nautobot/dcim/api/views.py +7 -46
  162. nautobot/dcim/choices.py +2 -25
  163. nautobot/dcim/elevations.py +0 -1
  164. nautobot/dcim/factory.py +15 -4
  165. nautobot/dcim/filters/__init__.py +42 -13
  166. nautobot/dcim/form_mixins.py +1 -27
  167. nautobot/dcim/forms.py +58 -797
  168. nautobot/dcim/management/commands/trace_paths.py +0 -1
  169. nautobot/dcim/migrations/0001_initial_part_1.py +0 -1
  170. nautobot/dcim/migrations/0002_initial_part_2.py +0 -1
  171. nautobot/dcim/migrations/0003_initial_part_3.py +0 -1
  172. nautobot/dcim/migrations/0004_initial_part_4.py +0 -1
  173. nautobot/dcim/migrations/0005_device_local_context_schema.py +0 -1
  174. nautobot/dcim/migrations/0006_auto_slug.py +0 -1
  175. nautobot/dcim/migrations/0007_device_secrets_group.py +0 -1
  176. nautobot/dcim/migrations/0008_increase_all_serial_lengths.py +0 -1
  177. nautobot/dcim/migrations/0009_add_natural_indexing.py +0 -1
  178. nautobot/dcim/migrations/0010_interface_status.py +0 -1
  179. nautobot/dcim/migrations/0011_interface_status_data_migration.py +0 -1
  180. nautobot/dcim/migrations/0012_interface_parent_bridge.py +0 -1
  181. nautobot/dcim/migrations/0013_location_location_type.py +0 -1
  182. nautobot/dcim/migrations/0014_location_status_data_migration.py +0 -1
  183. nautobot/dcim/migrations/0015_device_components__changeloggedmodel.py +0 -1
  184. nautobot/dcim/migrations/0016_device_components__timestamp_data_migration.py +0 -1
  185. nautobot/dcim/migrations/0017_locationtype_nestable.py +0 -1
  186. nautobot/dcim/migrations/0018_device_redundancy_group.py +0 -1
  187. nautobot/dcim/migrations/0019_device_redundancy_group_data_migration.py +0 -1
  188. nautobot/dcim/migrations/0020_move_site_fields_to_location_model.py +0 -1
  189. nautobot/dcim/migrations/0021_mptt_to_tree_queries.py +0 -1
  190. nautobot/dcim/migrations/0022_interface_mac_address_data_migration.py +0 -1
  191. nautobot/dcim/migrations/0023_alter_interface_mac_address.py +0 -1
  192. nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -2
  193. nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -14
  194. nautobot/dcim/migrations/0026_rename_device_and_rack_role.py +0 -1
  195. nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -2
  196. nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -2
  197. nautobot/dcim/migrations/0029_add_tree_managers_and_foreign_keys_pre_data_migration.py +0 -1
  198. nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
  199. nautobot/dcim/migrations/0031_rename_path_end_point_related_name.py +0 -1
  200. nautobot/dcim/migrations/0032_remove_site_foreign_key_from_dcim_models.py +0 -1
  201. nautobot/dcim/migrations/0033_created_datetime.py +0 -1
  202. nautobot/dcim/migrations/0034_fixup_fks_and_related_names.py +0 -1
  203. nautobot/dcim/migrations/0035_related_name_changes.py +1 -2
  204. nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -2
  205. nautobot/dcim/migrations/0037_interface_ip_addresses_m2m.py +0 -1
  206. nautobot/dcim/migrations/0038_alter_location_managers.py +0 -1
  207. nautobot/dcim/migrations/0039_remove_slug.py +24 -0
  208. nautobot/dcim/migrations/0040_tagsfield.py +109 -0
  209. nautobot/dcim/migrations/0041_ipam__namespaces.py +25 -0
  210. nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
  211. nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
  212. nautobot/dcim/models/cables.py +4 -35
  213. nautobot/dcim/models/device_component_templates.py +7 -2
  214. nautobot/dcim/models/device_components.py +26 -203
  215. nautobot/dcim/models/devices.py +30 -152
  216. nautobot/dcim/models/locations.py +3 -64
  217. nautobot/dcim/models/power.py +3 -51
  218. nautobot/dcim/models/racks.py +7 -86
  219. nautobot/dcim/navigation.py +141 -467
  220. nautobot/dcim/signals.py +0 -2
  221. nautobot/dcim/tables/devices.py +8 -5
  222. nautobot/dcim/tables/devicetypes.py +1 -1
  223. nautobot/dcim/tables/locations.py +2 -2
  224. nautobot/dcim/tables/power.py +2 -2
  225. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
  226. nautobot/dcim/templates/dcim/device.html +15 -4
  227. nautobot/dcim/templates/dcim/device_edit.html +6 -0
  228. nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +0 -1
  229. nautobot/dcim/templates/dcim/devicetype.html +2 -2
  230. nautobot/dcim/templates/dcim/interface.html +4 -0
  231. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
  232. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  233. nautobot/dcim/templates/dcim/location.html +16 -1
  234. nautobot/dcim/templates/dcim/locationtype.html +15 -0
  235. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
  236. nautobot/dcim/templates/dcim/rackgroup.html +0 -12
  237. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  238. nautobot/dcim/tests/test_api.py +202 -130
  239. nautobot/dcim/tests/test_cablepaths.py +47 -42
  240. nautobot/dcim/tests/test_filters.py +156 -134
  241. nautobot/dcim/tests/test_forms.py +12 -213
  242. nautobot/dcim/tests/test_graphql.py +8 -3
  243. nautobot/dcim/tests/test_migrations.py +6 -11
  244. nautobot/dcim/tests/test_models.py +208 -158
  245. nautobot/dcim/tests/test_natural_ordering.py +12 -14
  246. nautobot/dcim/tests/test_signals.py +7 -4
  247. nautobot/dcim/tests/test_views.py +270 -264
  248. nautobot/dcim/urls.py +21 -26
  249. nautobot/dcim/views.py +14 -156
  250. nautobot/docs/additional-features/caching.md +6 -87
  251. nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
  252. nautobot/docs/additional-features/jobs.md +179 -197
  253. nautobot/docs/administration/nautobot-server.md +9 -24
  254. nautobot/docs/administration/nautobot-shell.md +6 -6
  255. nautobot/docs/administration/replicating-nautobot.md +0 -10
  256. nautobot/docs/configuration/index.md +9 -9
  257. nautobot/docs/configuration/optional-settings.md +32 -61
  258. nautobot/docs/configuration/required-settings.md +11 -52
  259. nautobot/docs/development/application-registry.md +2 -13
  260. nautobot/docs/development/best-practices.md +2 -1
  261. nautobot/docs/development/docker-compose-advanced-use-cases.md +1 -1
  262. nautobot/docs/development/extending-models.md +15 -17
  263. nautobot/docs/development/generic-views.md +0 -2
  264. nautobot/docs/development/getting-started.md +56 -6
  265. nautobot/docs/development/navigation-menu.md +22 -93
  266. nautobot/docs/development/react-ui.md +105 -0
  267. nautobot/docs/development/release-checklist.md +3 -3
  268. nautobot/docs/development/role-internals.md +1 -3
  269. nautobot/docs/development/style-guide.md +6 -4
  270. nautobot/docs/development/templates.md +2 -1
  271. nautobot/docs/docker/index.md +16 -14
  272. nautobot/docs/index.md +7 -3
  273. nautobot/docs/installation/index.md +4 -1
  274. nautobot/docs/installation/migrating-from-netbox.md +12 -43
  275. nautobot/docs/installation/migrating-from-postgresql.md +1 -1
  276. nautobot/docs/installation/nautobot.md +1 -1
  277. nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  278. nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  279. nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  280. nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  281. nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
  282. nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  283. nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  284. nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  285. nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  286. nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  287. nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  288. nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  289. nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  290. nautobot/docs/installation/upgrading-from-nautobot-v1.md +190 -636
  291. nautobot/docs/installation/upgrading.md +5 -2
  292. nautobot/docs/models/dcim/device.md +3 -0
  293. nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
  294. nautobot/docs/models/extras/computedfield.md +4 -4
  295. nautobot/docs/models/extras/dynamicgroup.md +9 -9
  296. nautobot/docs/models/extras/gitrepository.md +3 -0
  297. nautobot/docs/models/extras/job.md +1 -0
  298. nautobot/docs/models/extras/jobbutton.md +18 -13
  299. nautobot/docs/models/extras/jobhook.md +7 -4
  300. nautobot/docs/models/extras/jobresult.md +6 -2
  301. nautobot/docs/models/extras/relationship.md +2 -2
  302. nautobot/docs/models/extras/status.md +6 -19
  303. nautobot/docs/models/ipam/ipaddress.md +3 -0
  304. nautobot/docs/models/ipam/vrf.md +0 -3
  305. nautobot/docs/models/virtualization/virtualmachine.md +3 -0
  306. nautobot/docs/plugins/development.md +92 -24
  307. nautobot/docs/release-notes/version-1.5.md +96 -0
  308. nautobot/docs/release-notes/version-2.0.md +216 -0
  309. nautobot/docs/requirements.txt +5 -4
  310. nautobot/docs/rest-api/overview.md +384 -215
  311. nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
  312. nautobot/extras/admin.py +3 -5
  313. nautobot/extras/api/customfields.py +15 -39
  314. nautobot/extras/api/fields.py +0 -11
  315. nautobot/extras/api/mixins.py +45 -0
  316. nautobot/extras/api/relationships.py +63 -159
  317. nautobot/extras/api/serializers.py +165 -706
  318. nautobot/extras/api/urls.py +1 -1
  319. nautobot/extras/api/views.py +295 -282
  320. nautobot/extras/apps.py +4 -7
  321. nautobot/extras/choices.py +11 -22
  322. nautobot/extras/constants.py +9 -3
  323. nautobot/extras/datasources/__init__.py +2 -0
  324. nautobot/extras/datasources/git.py +135 -186
  325. nautobot/extras/datasources/registry.py +25 -35
  326. nautobot/extras/factory.py +1 -3
  327. nautobot/extras/filters/__init__.py +49 -47
  328. nautobot/extras/filters/mixins.py +10 -8
  329. nautobot/extras/forms/forms.py +72 -148
  330. nautobot/extras/forms/mixins.py +34 -57
  331. nautobot/extras/health_checks.py +0 -33
  332. nautobot/extras/jobs.py +387 -566
  333. nautobot/extras/management/__init__.py +55 -48
  334. nautobot/extras/management/commands/renaturalize.py +0 -1
  335. nautobot/extras/management/commands/runjob.py +24 -62
  336. nautobot/extras/management/commands/webhook_receiver.py +0 -1
  337. nautobot/extras/managers.py +30 -7
  338. nautobot/extras/migrations/0001_initial_part_1.py +0 -1
  339. nautobot/extras/migrations/0002_initial_part_2.py +0 -1
  340. nautobot/extras/migrations/0003_initial_part_3.py +0 -1
  341. nautobot/extras/migrations/0004_populate_default_status_records.py +0 -1
  342. nautobot/extras/migrations/0005_configcontext_device_types.py +0 -1
  343. nautobot/extras/migrations/0006_graphqlquery.py +0 -1
  344. nautobot/extras/migrations/0007_configcontextschema.py +0 -1
  345. nautobot/extras/migrations/0008_jobresult__custom_field_data.py +0 -1
  346. nautobot/extras/migrations/0009_computedfield.py +0 -1
  347. nautobot/extras/migrations/0010_change_cf_validation_max_min_field_to_bigint.py +0 -1
  348. nautobot/extras/migrations/0011_fileattachment_fileproxy.py +0 -1
  349. nautobot/extras/migrations/0012_healthchecktestmodel.py +0 -1
  350. nautobot/extras/migrations/0013_default_fallback_value_computedfield.py +0 -1
  351. nautobot/extras/migrations/0014_auto_slug.py +0 -1
  352. nautobot/extras/migrations/0015_scheduled_job.py +0 -1
  353. nautobot/extras/migrations/0016_secret.py +0 -1
  354. nautobot/extras/migrations/0017_joblogentry.py +0 -1
  355. nautobot/extras/migrations/0018_joblog_data_migration.py +0 -2
  356. nautobot/extras/migrations/0019_joblogentry__meta_options__related_name.py +0 -1
  357. nautobot/extras/migrations/0020_customfield_changelog.py +0 -1
  358. nautobot/extras/migrations/0021_customfield_changelog_data.py +0 -1
  359. nautobot/extras/migrations/0022_objectchange_object_datav2.py +0 -1
  360. nautobot/extras/migrations/0023_job_model.py +0 -1
  361. nautobot/extras/migrations/0024_job_data_migration.py +0 -1
  362. nautobot/extras/migrations/0025_add_advanced_ui_boolean_to_customfield_conputedfield_and_relationship.py +0 -1
  363. nautobot/extras/migrations/0026_job_add_gitrepository_fk.py +0 -1
  364. nautobot/extras/migrations/0027_job_gitrepository_data_migration.py +0 -1
  365. nautobot/extras/migrations/0028_job_reduce_source.py +0 -1
  366. nautobot/extras/migrations/0029_dynamicgroup.py +0 -1
  367. nautobot/extras/migrations/0030_webhook_alter_unique_together.py +0 -1
  368. nautobot/extras/migrations/0031_tag_content_types.py +0 -1
  369. nautobot/extras/migrations/0032_tag_content_types_data_migration.py +0 -1
  370. nautobot/extras/migrations/0033_add__optimized_indexing.py +0 -1
  371. nautobot/extras/migrations/0034_alter_fileattachment_mimetype.py +0 -1
  372. nautobot/extras/migrations/0035_scheduledjob_crontab.py +0 -1
  373. nautobot/extras/migrations/0036_job_add_has_sensitive_variables.py +0 -1
  374. nautobot/extras/migrations/0037_configcontextschema__remove_name_unique__create_constraint_unique_name_owner.py +0 -1
  375. nautobot/extras/migrations/0038_configcontext_locations.py +0 -1
  376. nautobot/extras/migrations/0039_objectchange__add_change_context.py +0 -1
  377. nautobot/extras/migrations/0040_dynamicgroup__dynamicgroupmembership.py +0 -1
  378. nautobot/extras/migrations/0041_jobresult_job_kwargs.py +0 -1
  379. nautobot/extras/migrations/0042_job__add_is_job_hook_receiver.py +0 -1
  380. nautobot/extras/migrations/0043_note.py +0 -1
  381. nautobot/extras/migrations/0044_add_job_hook.py +0 -1
  382. nautobot/extras/migrations/0045_add_custom_field_slug.py +0 -1
  383. nautobot/extras/migrations/0046_populate_custom_field_slug_label.py +0 -1
  384. nautobot/extras/migrations/0047_enforce_custom_field_slug.py +0 -1
  385. nautobot/extras/migrations/0048_alter_objectchange_change_context_detail.py +0 -1
  386. nautobot/extras/migrations/0049_alter_tag_slug.py +0 -1
  387. nautobot/extras/migrations/0050_customfield_grouping.py +0 -1
  388. nautobot/extras/migrations/0051_add_job_task_queues.py +0 -1
  389. nautobot/extras/migrations/0052_configcontext_device_redundancy_groups.py +0 -1
  390. nautobot/extras/migrations/0053_relationship_required_on.py +0 -1
  391. nautobot/extras/migrations/0054_scheduledjob_kwargs_request_user_change.py +0 -1
  392. nautobot/extras/migrations/0055_configcontext_dynamic_groups.py +0 -1
  393. nautobot/extras/migrations/0056_objectchange_add_reverse_time_idx.py +0 -1
  394. nautobot/extras/migrations/0057_jobbutton.py +0 -1
  395. nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
  396. nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -2
  397. nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -2
  398. nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -8
  399. nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -33
  400. nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -2
  401. nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -2
  402. nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
  403. nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -2
  404. nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -3
  405. nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -2
  406. nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -2
  407. nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
  408. nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -2
  409. nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
  410. nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -10
  411. nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -2
  412. nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
  413. nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
  414. nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
  415. nautobot/extras/migrations/0078_remove_slug.py +45 -0
  416. nautobot/extras/migrations/0079_tagsfield.py +28 -0
  417. nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
  418. nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
  419. nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
  420. nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
  421. nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
  422. nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
  423. nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
  424. nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
  425. nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
  426. nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
  427. nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
  428. nautobot/extras/models/__init__.py +2 -3
  429. nautobot/extras/models/change_logging.py +0 -36
  430. nautobot/extras/models/customfields.py +39 -33
  431. nautobot/extras/models/datasources.py +48 -50
  432. nautobot/extras/models/groups.py +5 -12
  433. nautobot/extras/models/jobs.py +190 -323
  434. nautobot/extras/models/mixins.py +0 -71
  435. nautobot/extras/models/models.py +1 -22
  436. nautobot/extras/models/relationships.py +20 -21
  437. nautobot/extras/models/roles.py +0 -23
  438. nautobot/extras/models/secrets.py +2 -31
  439. nautobot/extras/models/statuses.py +6 -5
  440. nautobot/extras/models/tags.py +2 -17
  441. nautobot/extras/navigation.py +89 -307
  442. nautobot/extras/plugins/__init__.py +3 -121
  443. nautobot/extras/plugins/utils.py +0 -3
  444. nautobot/extras/plugins/validators.py +5 -4
  445. nautobot/extras/plugins/views.py +16 -4
  446. nautobot/extras/querysets.py +1 -7
  447. nautobot/extras/registry.py +3 -0
  448. nautobot/extras/signals.py +26 -60
  449. nautobot/extras/tables.py +42 -49
  450. nautobot/extras/tasks.py +0 -12
  451. nautobot/extras/templates/extras/configcontext.html +1 -1
  452. nautobot/extras/templates/extras/configcontextschema.html +16 -1
  453. nautobot/extras/templates/extras/customfield.html +0 -13
  454. nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -1
  455. nautobot/extras/templates/extras/gitrepository.html +3 -3
  456. nautobot/extras/templates/extras/inc/jobresult.html +10 -0
  457. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  458. nautobot/extras/templates/extras/job.html +35 -25
  459. nautobot/extras/templates/extras/job_approval_request.html +15 -30
  460. nautobot/extras/templates/extras/job_detail.html +13 -31
  461. nautobot/extras/templates/extras/job_edit.html +14 -17
  462. nautobot/extras/templates/extras/jobresult.html +24 -6
  463. nautobot/extras/templates/extras/objectchange_list.html +1 -1
  464. nautobot/extras/templates/extras/scheduledjob.html +2 -2
  465. nautobot/extras/templates/extras/secret.html +28 -0
  466. nautobot/extras/templates/extras/secret_edit.html +0 -1
  467. nautobot/extras/templates/extras/secretsgroup_edit.html +0 -1
  468. nautobot/extras/templatetags/custom_links.py +0 -2
  469. nautobot/extras/templatetags/job_buttons.py +1 -0
  470. nautobot/extras/templatetags/plugins.py +0 -1
  471. nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
  472. nautobot/extras/test_jobs/atomic_transaction.py +53 -0
  473. nautobot/extras/test_jobs/dry_run.py +29 -0
  474. nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
  475. nautobot/extras/test_jobs/duplicate_name2.py +9 -0
  476. nautobot/extras/test_jobs/fail.py +23 -0
  477. nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
  478. nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
  479. nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
  480. nautobot/extras/test_jobs/file_upload_pass.py +25 -0
  481. nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
  482. nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
  483. nautobot/extras/test_jobs/job_button_receiver.py +28 -0
  484. nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
  485. nautobot/extras/test_jobs/job_variables.py +88 -0
  486. nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
  487. nautobot/extras/test_jobs/log_redaction.py +20 -0
  488. nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
  489. nautobot/extras/test_jobs/modify_db.py +25 -0
  490. nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
  491. nautobot/extras/test_jobs/object_var_optional.py +21 -0
  492. nautobot/extras/test_jobs/object_var_required.py +21 -0
  493. nautobot/extras/test_jobs/object_vars.py +26 -0
  494. nautobot/extras/test_jobs/pass.py +25 -0
  495. nautobot/extras/test_jobs/profiling.py +32 -0
  496. nautobot/extras/test_jobs/read_only_job.py +15 -0
  497. nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
  498. nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
  499. nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
  500. nautobot/extras/tests/integration/__init__.py +3 -3
  501. nautobot/extras/tests/integration/test_computedfields.py +1 -1
  502. nautobot/extras/tests/integration/test_configcontextschema.py +7 -5
  503. nautobot/extras/tests/integration/test_customfields.py +4 -2
  504. nautobot/extras/tests/integration/test_dynamicgroups.py +2 -2
  505. nautobot/extras/tests/integration/test_jobs.py +25 -27
  506. nautobot/extras/tests/integration/test_notes.py +8 -4
  507. nautobot/extras/tests/integration/test_plugins.py +4 -4
  508. nautobot/extras/tests/integration/test_relationships.py +2 -2
  509. nautobot/extras/tests/test_api.py +371 -381
  510. nautobot/extras/tests/test_changelog.py +17 -16
  511. nautobot/extras/tests/test_context_managers.py +5 -6
  512. nautobot/extras/tests/test_customfields.py +112 -73
  513. nautobot/extras/tests/test_datasources.py +191 -117
  514. nautobot/extras/tests/test_dynamicgroups.py +45 -68
  515. nautobot/extras/tests/test_filters.py +170 -130
  516. nautobot/extras/tests/test_forms.py +107 -109
  517. nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
  518. nautobot/extras/tests/test_jobs.py +271 -273
  519. nautobot/extras/tests/test_management.py +3 -6
  520. nautobot/extras/tests/test_migrations.py +5 -3
  521. nautobot/extras/tests/test_models.py +121 -173
  522. nautobot/extras/tests/test_notes.py +0 -1
  523. nautobot/extras/tests/test_plugins.py +55 -89
  524. nautobot/extras/tests/test_relationships.py +174 -130
  525. nautobot/extras/tests/test_tags.py +6 -12
  526. nautobot/extras/tests/test_utils.py +31 -1
  527. nautobot/extras/tests/test_views.py +223 -184
  528. nautobot/extras/tests/test_webhooks.py +16 -15
  529. nautobot/extras/urls.py +69 -69
  530. nautobot/extras/utils.py +137 -163
  531. nautobot/extras/views.py +81 -153
  532. nautobot/ipam/api/fields.py +17 -0
  533. nautobot/ipam/api/serializers.py +77 -164
  534. nautobot/ipam/api/urls.py +4 -1
  535. nautobot/ipam/api/views.py +28 -19
  536. nautobot/ipam/apps.py +1 -0
  537. nautobot/ipam/choices.py +5 -12
  538. nautobot/ipam/constants.py +1 -0
  539. nautobot/ipam/factory.py +41 -30
  540. nautobot/ipam/filters.py +58 -25
  541. nautobot/ipam/forms.py +82 -211
  542. nautobot/ipam/graphql/types.py +0 -9
  543. nautobot/ipam/lookups.py +13 -8
  544. nautobot/ipam/management/commands/__init__.py +0 -0
  545. nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
  546. nautobot/ipam/migrations/0001_initial_part_1.py +0 -1
  547. nautobot/ipam/migrations/0002_initial_part_2.py +0 -1
  548. nautobot/ipam/migrations/0003_remove_max_length.py +0 -1
  549. nautobot/ipam/migrations/0004_fixup_p2p_broadcast.py +0 -1
  550. nautobot/ipam/migrations/0005_auto_slug.py +0 -1
  551. nautobot/ipam/migrations/0006_ipaddress_nat_outside_list.py +0 -1
  552. nautobot/ipam/migrations/0007_add_natural_indexing.py +0 -1
  553. nautobot/ipam/migrations/0008_prefix_vlan_vlangroup_location.py +0 -1
  554. nautobot/ipam/migrations/0009_alter_vlan_name.py +0 -1
  555. nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -2
  556. nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -39
  557. nautobot/ipam/migrations/0012_rename_ipam_roles.py +0 -1
  558. nautobot/ipam/migrations/0013_delete_role.py +0 -1
  559. nautobot/ipam/migrations/0014_rename_foreign_keys_and_related_names.py +0 -1
  560. nautobot/ipam/migrations/0015_prefix_add_type.py +0 -1
  561. nautobot/ipam/migrations/0016_prefix_type_data_migration.py +0 -3
  562. nautobot/ipam/migrations/0017_prefix_remove_is_pool.py +0 -1
  563. nautobot/ipam/migrations/0018_remove_site_foreign_key_from_ipam_models.py +0 -1
  564. nautobot/ipam/migrations/0019_created_datetime.py +0 -1
  565. nautobot/ipam/migrations/0020_related_name_changes.py +1 -2
  566. nautobot/ipam/migrations/0021_prefix_add_rir_and_date_allocated.py +0 -1
  567. nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +3 -5
  568. nautobot/ipam/migrations/0023_delete_aggregate.py +0 -1
  569. nautobot/ipam/migrations/0024_interface_to_ipaddress_m2m.py +0 -1
  570. nautobot/ipam/migrations/0025_interface_ipaddress_m2m_data_migration.py +0 -1
  571. nautobot/ipam/migrations/0026_ipaddress_remove_assigned_object.py +0 -1
  572. nautobot/ipam/migrations/0027_remove_rir_slug.py +16 -0
  573. nautobot/ipam/migrations/0028_tagsfield.py +44 -0
  574. nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
  575. nautobot/ipam/migrations/0030_ipam__namespaces.py +231 -0
  576. nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
  577. nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
  578. nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
  579. nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
  580. nautobot/ipam/models.py +579 -368
  581. nautobot/ipam/navigation.py +36 -159
  582. nautobot/ipam/querysets.py +117 -90
  583. nautobot/ipam/signals.py +89 -0
  584. nautobot/ipam/tables.py +86 -28
  585. nautobot/ipam/templates/ipam/ipaddress.html +14 -30
  586. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -0
  587. nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
  588. nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
  589. nautobot/ipam/templates/ipam/namespace_retrieve.html +42 -0
  590. nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
  591. nautobot/ipam/templates/ipam/prefix.html +27 -33
  592. nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
  593. nautobot/ipam/templates/ipam/vlangroup.html +0 -13
  594. nautobot/ipam/templates/ipam/vrf.html +6 -4
  595. nautobot/ipam/templates/ipam/vrf_edit.html +20 -2
  596. nautobot/ipam/tests/integration/test_prefixes.py +4 -27
  597. nautobot/ipam/tests/test_api.py +60 -61
  598. nautobot/ipam/tests/test_filters.py +187 -126
  599. nautobot/ipam/tests/test_forms.py +12 -6
  600. nautobot/ipam/tests/test_graphql.py +8 -6
  601. nautobot/ipam/tests/test_migrations.py +8 -13
  602. nautobot/ipam/tests/test_models.py +426 -274
  603. nautobot/ipam/tests/test_ordering.py +6 -3
  604. nautobot/ipam/tests/test_querysets.py +340 -96
  605. nautobot/ipam/tests/test_views.py +100 -55
  606. nautobot/ipam/urls.py +28 -5
  607. nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
  608. nautobot/ipam/utils/migrations.py +713 -0
  609. nautobot/ipam/views.py +237 -122
  610. nautobot/project-static/docs/404.html +1399 -166
  611. nautobot/project-static/docs/additional-features/caching.html +1416 -320
  612. nautobot/project-static/docs/additional-features/change-logging.html +1389 -190
  613. nautobot/project-static/docs/additional-features/config-contexts.html +1389 -190
  614. nautobot/project-static/docs/additional-features/graphql.html +1389 -190
  615. nautobot/project-static/docs/additional-features/healthcheck.html +1389 -190
  616. nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1393 -190
  617. nautobot/project-static/docs/additional-features/jobs.html +1677 -460
  618. nautobot/project-static/docs/additional-features/napalm.html +1389 -190
  619. nautobot/project-static/docs/additional-features/prometheus-metrics.html +1389 -190
  620. nautobot/project-static/docs/additional-features/template-filters.html +1389 -190
  621. nautobot/project-static/docs/administration/celery-queues.html +1389 -190
  622. nautobot/project-static/docs/administration/nautobot-server.html +1553 -375
  623. nautobot/project-static/docs/administration/nautobot-shell.html +1395 -196
  624. nautobot/project-static/docs/administration/permissions.html +1389 -190
  625. nautobot/project-static/docs/administration/replicating-nautobot.html +1387 -207
  626. nautobot/project-static/docs/apps/index.html +1389 -190
  627. nautobot/project-static/docs/apps/nautobot-apps.html +1387 -175
  628. nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js +29 -0
  629. nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js.map +8 -0
  630. nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js → search.208ed371.min.js} +9 -15
  631. nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js.map → search.208ed371.min.js.map} +4 -4
  632. nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css +1 -0
  633. nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css.map +1 -0
  634. nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css +1 -0
  635. nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css.map +1 -0
  636. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1775 -590
  637. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1389 -190
  638. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3588 -1922
  639. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1461 -262
  640. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1401 -170
  641. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1396 -191
  642. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +2095 -894
  643. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2357 -1194
  644. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2258 -940
  645. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1389 -190
  646. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1400 -201
  647. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +11068 -7861
  648. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2867 -2224
  649. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1389 -190
  650. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2641 -1573
  651. nautobot/project-static/docs/configuration/authentication/ldap.html +1389 -190
  652. nautobot/project-static/docs/configuration/authentication/remote.html +1389 -190
  653. nautobot/project-static/docs/configuration/authentication/sso.html +1389 -190
  654. nautobot/project-static/docs/configuration/index.html +1398 -199
  655. nautobot/project-static/docs/configuration/optional-settings.html +1418 -274
  656. nautobot/project-static/docs/configuration/required-settings.html +1419 -287
  657. nautobot/project-static/docs/core-functionality/circuits.html +1446 -247
  658. nautobot/project-static/docs/core-functionality/device-types.html +1448 -249
  659. nautobot/project-static/docs/core-functionality/devices.html +1452 -249
  660. nautobot/project-static/docs/core-functionality/ipam.html +1452 -253
  661. nautobot/project-static/docs/core-functionality/power.html +1448 -249
  662. nautobot/project-static/docs/core-functionality/secrets.html +1448 -249
  663. nautobot/project-static/docs/core-functionality/services.html +1448 -249
  664. nautobot/project-static/docs/core-functionality/sites-and-racks.html +1448 -249
  665. nautobot/project-static/docs/core-functionality/tenancy.html +1448 -249
  666. nautobot/project-static/docs/core-functionality/virtualization.html +1452 -249
  667. nautobot/project-static/docs/core-functionality/vlans.html +1448 -249
  668. nautobot/project-static/docs/development/application-registry.html +1393 -214
  669. nautobot/project-static/docs/development/best-practices.html +1392 -192
  670. nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1390 -191
  671. nautobot/project-static/docs/development/extending-models.html +1443 -257
  672. nautobot/project-static/docs/development/generic-views.html +1403 -174
  673. nautobot/project-static/docs/development/getting-started.html +1568 -262
  674. nautobot/project-static/docs/development/homepage.html +1389 -190
  675. nautobot/project-static/docs/development/index.html +1389 -190
  676. nautobot/project-static/docs/development/model-features.html +1389 -190
  677. nautobot/project-static/docs/development/natural-keys.html +1389 -190
  678. nautobot/project-static/docs/development/navigation-menu.html +1451 -330
  679. nautobot/project-static/docs/development/react-ui.html +4199 -0
  680. nautobot/project-static/docs/development/release-checklist.html +1392 -193
  681. nautobot/project-static/docs/development/role-internals.html +1402 -172
  682. nautobot/project-static/docs/development/style-guide.html +1399 -199
  683. nautobot/project-static/docs/development/templates.html +1391 -191
  684. nautobot/project-static/docs/development/testing.html +1389 -190
  685. nautobot/project-static/docs/development/user-preferences.html +1389 -190
  686. nautobot/project-static/docs/docker/index.html +1408 -206
  687. nautobot/project-static/docs/index.html +1397 -180
  688. nautobot/project-static/docs/installation/centos.html +1401 -170
  689. nautobot/project-static/docs/installation/external-authentication.html +1389 -190
  690. nautobot/project-static/docs/installation/http-server.html +1389 -190
  691. nautobot/project-static/docs/installation/index.html +1394 -191
  692. nautobot/project-static/docs/installation/migrating-from-netbox.html +1452 -305
  693. nautobot/project-static/docs/installation/migrating-from-postgresql.html +1390 -191
  694. nautobot/project-static/docs/installation/nautobot.html +1390 -191
  695. nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1389 -190
  696. nautobot/project-static/docs/installation/selinux-troubleshooting.html +1401 -170
  697. nautobot/project-static/docs/installation/services.html +1389 -190
  698. nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  699. nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  700. nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  701. nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  702. nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
  703. nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  704. nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  705. nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  706. nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  707. nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  708. nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  709. nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  710. nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  711. nautobot/project-static/docs/installation/ubuntu.html +1401 -170
  712. nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +4254 -1923
  713. nautobot/project-static/docs/installation/upgrading.html +1395 -192
  714. nautobot/project-static/docs/models/circuits/circuit.html +1427 -174
  715. nautobot/project-static/docs/models/circuits/circuittermination.html +1427 -174
  716. nautobot/project-static/docs/models/circuits/circuittype.html +1427 -174
  717. nautobot/project-static/docs/models/circuits/provider.html +1427 -174
  718. nautobot/project-static/docs/models/circuits/providernetwork.html +1427 -174
  719. nautobot/project-static/docs/models/dcim/cable.html +1458 -174
  720. nautobot/project-static/docs/models/dcim/consoleport.html +1427 -174
  721. nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1427 -174
  722. nautobot/project-static/docs/models/dcim/consoleserverport.html +1427 -174
  723. nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1427 -174
  724. nautobot/project-static/docs/models/dcim/device.html +1431 -174
  725. nautobot/project-static/docs/models/dcim/devicebay.html +1427 -174
  726. nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1427 -174
  727. nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1522 -177
  728. nautobot/project-static/docs/models/dcim/devicetype.html +1427 -174
  729. nautobot/project-static/docs/models/dcim/frontport.html +1427 -174
  730. nautobot/project-static/docs/models/dcim/frontporttemplate.html +1427 -174
  731. nautobot/project-static/docs/models/dcim/interface.html +1427 -174
  732. nautobot/project-static/docs/models/dcim/interfacetemplate.html +1427 -174
  733. nautobot/project-static/docs/models/dcim/inventoryitem.html +1427 -174
  734. nautobot/project-static/docs/models/dcim/location.html +1427 -174
  735. nautobot/project-static/docs/models/dcim/locationtype.html +1427 -174
  736. nautobot/project-static/docs/models/dcim/manufacturer.html +1427 -174
  737. nautobot/project-static/docs/models/dcim/platform.html +1427 -174
  738. nautobot/project-static/docs/models/dcim/powerfeed.html +1425 -172
  739. nautobot/project-static/docs/models/dcim/poweroutlet.html +1427 -174
  740. nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1427 -174
  741. nautobot/project-static/docs/models/dcim/powerpanel.html +1425 -172
  742. nautobot/project-static/docs/models/dcim/powerport.html +1427 -174
  743. nautobot/project-static/docs/models/dcim/powerporttemplate.html +1427 -174
  744. nautobot/project-static/docs/models/dcim/rack.html +1427 -174
  745. nautobot/project-static/docs/models/dcim/rackgroup.html +1427 -174
  746. nautobot/project-static/docs/models/dcim/rackreservation.html +1427 -174
  747. nautobot/project-static/docs/models/dcim/rearport.html +1427 -174
  748. nautobot/project-static/docs/models/dcim/rearporttemplate.html +1427 -174
  749. nautobot/project-static/docs/models/dcim/region.html +1401 -170
  750. nautobot/project-static/docs/models/dcim/site.html +1401 -170
  751. nautobot/project-static/docs/models/dcim/virtualchassis.html +1425 -172
  752. nautobot/project-static/docs/models/extras/computedfield.html +1393 -194
  753. nautobot/project-static/docs/models/extras/configcontext.html +1465 -174
  754. nautobot/project-static/docs/models/extras/configcontextschema.html +1421 -168
  755. nautobot/project-static/docs/models/extras/customfield.html +1389 -190
  756. nautobot/project-static/docs/models/extras/customlink.html +1389 -190
  757. nautobot/project-static/docs/models/extras/dynamicgroup.html +1398 -199
  758. nautobot/project-static/docs/models/extras/exporttemplate.html +1389 -190
  759. nautobot/project-static/docs/models/extras/gitrepository.html +1393 -190
  760. nautobot/project-static/docs/models/extras/graphqlquery.html +1469 -171
  761. nautobot/project-static/docs/models/extras/imageattachment.html +1434 -181
  762. nautobot/project-static/docs/models/extras/job.html +1411 -157
  763. nautobot/project-static/docs/models/extras/jobbutton.html +1410 -207
  764. nautobot/project-static/docs/models/extras/jobhook.html +1397 -194
  765. nautobot/project-static/docs/models/extras/joblogentry.html +1408 -155
  766. nautobot/project-static/docs/models/extras/jobresult.html +1417 -159
  767. nautobot/project-static/docs/models/extras/note.html +1389 -190
  768. nautobot/project-static/docs/models/extras/relationship.html +1391 -192
  769. nautobot/project-static/docs/models/extras/role.html +1495 -198
  770. nautobot/project-static/docs/models/extras/secret.html +1492 -201
  771. nautobot/project-static/docs/models/extras/secretsgroup.html +1410 -157
  772. nautobot/project-static/docs/models/extras/status.html +1381 -221
  773. nautobot/project-static/docs/models/extras/tag.html +1389 -190
  774. nautobot/project-static/docs/models/extras/webhook.html +1389 -190
  775. nautobot/project-static/docs/models/ipam/ipaddress.html +1488 -200
  776. nautobot/project-static/docs/models/ipam/prefix.html +1410 -157
  777. nautobot/project-static/docs/models/ipam/rir.html +1410 -157
  778. nautobot/project-static/docs/models/ipam/routetarget.html +1410 -157
  779. nautobot/project-static/docs/models/ipam/service.html +1410 -157
  780. nautobot/project-static/docs/models/ipam/vlan.html +1410 -157
  781. nautobot/project-static/docs/models/ipam/vlangroup.html +1410 -157
  782. nautobot/project-static/docs/models/ipam/vrf.html +1410 -161
  783. nautobot/project-static/docs/models/tenancy/tenant.html +1412 -159
  784. nautobot/project-static/docs/models/tenancy/tenantgroup.html +1412 -159
  785. nautobot/project-static/docs/models/users/objectpermission.html +1462 -171
  786. nautobot/project-static/docs/models/users/token.html +1410 -157
  787. nautobot/project-static/docs/models/virtualization/cluster.html +1410 -157
  788. nautobot/project-static/docs/models/virtualization/clustergroup.html +1410 -157
  789. nautobot/project-static/docs/models/virtualization/clustertype.html +1410 -157
  790. nautobot/project-static/docs/models/virtualization/virtualmachine.html +1414 -157
  791. nautobot/project-static/docs/models/virtualization/vminterface.html +1410 -157
  792. nautobot/project-static/docs/objects.inv +0 -0
  793. nautobot/project-static/docs/plugins/development.html +1916 -646
  794. nautobot/project-static/docs/plugins/index.html +1389 -190
  795. nautobot/project-static/docs/plugins/porting-from-netbox.html +1389 -190
  796. nautobot/project-static/docs/release-notes/index.html +1389 -190
  797. nautobot/project-static/docs/release-notes/version-1.0.html +1389 -190
  798. nautobot/project-static/docs/release-notes/version-1.1.html +1389 -190
  799. nautobot/project-static/docs/release-notes/version-1.2.html +1389 -190
  800. nautobot/project-static/docs/release-notes/version-1.3.html +1389 -190
  801. nautobot/project-static/docs/release-notes/version-1.4.html +1389 -190
  802. nautobot/project-static/docs/release-notes/version-1.5.html +2016 -397
  803. nautobot/project-static/docs/release-notes/version-2.0.html +1935 -287
  804. nautobot/project-static/docs/requirements.txt +5 -4
  805. nautobot/project-static/docs/rest-api/authentication.html +1389 -190
  806. nautobot/project-static/docs/rest-api/filtering.html +1389 -190
  807. nautobot/project-static/docs/rest-api/overview.html +2002 -576
  808. nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
  809. nautobot/project-static/docs/search/search_index.json +1 -1
  810. nautobot/project-static/docs/sitemap.xml +197 -187
  811. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  812. nautobot/project-static/docs/user-guides/custom-fields.html +1390 -191
  813. nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1392 -193
  814. nautobot/project-static/docs/user-guides/getting-started/index.html +1388 -189
  815. nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1388 -189
  816. nautobot/project-static/docs/user-guides/getting-started/ipam.html +1386 -187
  817. nautobot/project-static/docs/user-guides/getting-started/platforms.html +1448 -249
  818. nautobot/project-static/docs/user-guides/getting-started/regions.html +1411 -212
  819. nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1395 -196
  820. nautobot/project-static/docs/user-guides/getting-started/tenants.html +1448 -249
  821. nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1448 -249
  822. nautobot/project-static/docs/user-guides/git-data-source.html +1405 -206
  823. nautobot/project-static/docs/user-guides/graphql.html +1402 -203
  824. nautobot/project-static/docs/user-guides/relationships.html +1448 -249
  825. nautobot/project-static/docs/user-guides/s3-django-storage.html +1448 -249
  826. nautobot/project-static/js/forms.js +16 -9
  827. nautobot/project-static/js/theme.js +5 -0
  828. nautobot/tenancy/api/serializers.py +4 -34
  829. nautobot/tenancy/api/urls.py +1 -1
  830. nautobot/tenancy/filters/__init__.py +9 -7
  831. nautobot/tenancy/filters/mixins.py +3 -2
  832. nautobot/tenancy/forms.py +3 -36
  833. nautobot/tenancy/migrations/0001_initial.py +0 -1
  834. nautobot/tenancy/migrations/0002_auto_slug.py +0 -1
  835. nautobot/tenancy/migrations/0003_mptt_to_tree_queries.py +0 -1
  836. nautobot/tenancy/migrations/0004_change_tree_manager_on_tree_models.py +0 -1
  837. nautobot/tenancy/migrations/0005_rename_foreign_keys_and_related_names.py +0 -1
  838. nautobot/tenancy/migrations/0006_created_datetime.py +0 -1
  839. nautobot/tenancy/migrations/0007_remove_tenant_tenantgroup_slug.py +20 -0
  840. nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
  841. nautobot/tenancy/models.py +0 -30
  842. nautobot/tenancy/navigation.py +6 -39
  843. nautobot/tenancy/tables.py +4 -4
  844. nautobot/tenancy/templates/tenancy/tenant.html +12 -12
  845. nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -1
  846. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  847. nautobot/tenancy/tests/test_api.py +1 -12
  848. nautobot/tenancy/tests/test_filters.py +20 -12
  849. nautobot/tenancy/tests/test_views.py +11 -29
  850. nautobot/tenancy/urls.py +10 -10
  851. nautobot/tenancy/views.py +0 -3
  852. nautobot/ui/.eslintignore +6 -0
  853. nautobot/ui/.gitignore +10 -0
  854. nautobot/ui/.prettierignore +9 -0
  855. nautobot/ui/.prettierrc +4 -0
  856. nautobot/ui/README.md +33 -0
  857. nautobot/ui/app_imports.js.j2 +7 -0
  858. nautobot/ui/craco.config.js +46 -0
  859. nautobot/ui/jsconfig-base.json +11 -0
  860. nautobot/ui/jsconfig.json +5 -0
  861. nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
  862. nautobot/ui/package-lock.json +21451 -0
  863. nautobot/ui/package.json +70 -0
  864. nautobot/ui/public/index.html +47 -0
  865. nautobot/ui/public/logo192.png +0 -0
  866. nautobot/ui/public/logo512.png +0 -0
  867. nautobot/ui/public/manifest.json +25 -0
  868. nautobot/ui/public/nautobot_logo.svg +131 -0
  869. nautobot/ui/public/robots.txt +3 -0
  870. nautobot/ui/src/App.js +71 -0
  871. nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
  872. nautobot/ui/src/components/AppTab.js +40 -0
  873. nautobot/ui/src/components/Apps.js +60 -0
  874. nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
  875. nautobot/ui/src/components/HomePanel.js +58 -0
  876. nautobot/ui/src/components/JobHistoryTable.js +78 -0
  877. nautobot/ui/src/components/Layout.js +53 -0
  878. nautobot/ui/src/components/LoadingWidget.js +25 -0
  879. nautobot/ui/src/components/Navbar.js +116 -0
  880. nautobot/ui/src/components/NotificationPopover.js +27 -0
  881. nautobot/ui/src/components/ObjectListTable.js +209 -0
  882. nautobot/ui/src/components/ReferenceDataTag.js +35 -0
  883. nautobot/ui/src/components/RouterButton.js +10 -0
  884. nautobot/ui/src/components/RouterLink.js +10 -0
  885. nautobot/ui/src/components/SidebarNav.js +147 -0
  886. nautobot/ui/src/components/Table.js +48 -0
  887. nautobot/ui/src/components/TableItem.js +71 -0
  888. nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
  889. nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
  890. nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
  891. nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
  892. nautobot/ui/src/components/__tests__/Table.test.js +36 -0
  893. nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
  894. nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
  895. nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
  896. nautobot/ui/src/components/pagination.js +93 -0
  897. nautobot/ui/src/components/paginator.js +79 -0
  898. nautobot/ui/src/components/paginator_form.js +43 -0
  899. nautobot/ui/src/components/usePagination.js +57 -0
  900. nautobot/ui/src/constants/apiPath.js +10 -0
  901. nautobot/ui/src/constants/icons.js +15 -0
  902. nautobot/ui/src/constants/size.js +15 -0
  903. nautobot/ui/src/index.js +65 -0
  904. nautobot/ui/src/reportWebVitals.js +15 -0
  905. nautobot/ui/src/router.js +77 -0
  906. nautobot/ui/src/utils/api.js +131 -0
  907. nautobot/ui/src/utils/app-import.js +15 -0
  908. nautobot/ui/src/utils/color.js +15 -0
  909. nautobot/ui/src/utils/date.js +14 -0
  910. nautobot/ui/src/utils/index.js +15 -0
  911. nautobot/ui/src/utils/navigation.js +32 -0
  912. nautobot/ui/src/utils/session.js +64 -0
  913. nautobot/ui/src/utils/store.js +242 -0
  914. nautobot/ui/src/utils/string.js +6 -0
  915. nautobot/ui/src/utils/url.js +4 -0
  916. nautobot/ui/src/views/Home.js +138 -0
  917. nautobot/ui/src/views/InstalledApps.js +80 -0
  918. nautobot/ui/src/views/Login.js +48 -0
  919. nautobot/ui/src/views/Logout.js +20 -0
  920. nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
  921. nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
  922. nautobot/ui/src/views/__tests__/Login.test.js +15 -0
  923. nautobot/ui/src/views/generic/GenericView.js +142 -0
  924. nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
  925. nautobot/ui/src/views/generic/ObjectList.js +127 -0
  926. nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
  927. nautobot/users/admin.py +1 -1
  928. nautobot/users/api/serializers.py +51 -61
  929. nautobot/users/api/urls.py +1 -1
  930. nautobot/users/api/views.py +53 -2
  931. nautobot/users/migrations/0001_initial.py +0 -1
  932. nautobot/users/migrations/0002_token_ordering_by_created.py +0 -1
  933. nautobot/users/migrations/0003_alter_user_options.py +0 -1
  934. nautobot/users/migrations/0004_alter_user_managers.py +0 -1
  935. nautobot/users/tests/test_api.py +109 -28
  936. nautobot/users/tests/test_filters.py +0 -4
  937. nautobot/users/tests/test_models.py +0 -1
  938. nautobot/users/views.py +0 -7
  939. nautobot/virtualization/api/serializers.py +18 -132
  940. nautobot/virtualization/api/urls.py +1 -1
  941. nautobot/virtualization/api/views.py +1 -22
  942. nautobot/virtualization/choices.py +0 -2
  943. nautobot/virtualization/filters.py +12 -7
  944. nautobot/virtualization/forms.py +21 -117
  945. nautobot/virtualization/migrations/0001_initial.py +0 -1
  946. nautobot/virtualization/migrations/0002_virtualmachine_local_context_schema.py +0 -1
  947. nautobot/virtualization/migrations/0003_vminterface_verbose_name.py +0 -1
  948. nautobot/virtualization/migrations/0004_auto_slug.py +0 -1
  949. nautobot/virtualization/migrations/0005_add_natural_indexing.py +0 -1
  950. nautobot/virtualization/migrations/0006_vminterface_status.py +0 -1
  951. nautobot/virtualization/migrations/0007_vminterface_status_data_migration.py +0 -1
  952. nautobot/virtualization/migrations/0008_vminterface_parent.py +0 -1
  953. nautobot/virtualization/migrations/0009_cluster_location.py +0 -1
  954. nautobot/virtualization/migrations/0010_vminterface_mac_address_data_migration.py +0 -1
  955. nautobot/virtualization/migrations/0011_alter_vminterface_mac_address.py +0 -1
  956. nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -2
  957. nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -12
  958. nautobot/virtualization/migrations/0014_rename_virtualmachine_roles.py +0 -1
  959. nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -2
  960. nautobot/virtualization/migrations/0016_remove_site_foreign_key_from_cluster_class.py +0 -1
  961. nautobot/virtualization/migrations/0017_created_datetime.py +0 -1
  962. nautobot/virtualization/migrations/0018_related_name_changes.py +1 -2
  963. nautobot/virtualization/migrations/0019_vminterface_ip_addresses_m2m.py +0 -1
  964. nautobot/virtualization/migrations/0020_remove_clustergroup_clustertype_slug.py +20 -0
  965. nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
  966. nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
  967. nautobot/virtualization/migrations/0023_ipam__namespaces.py +25 -0
  968. nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
  969. nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
  970. nautobot/virtualization/models.py +39 -131
  971. nautobot/virtualization/navigation.py +18 -99
  972. nautobot/virtualization/tables.py +4 -4
  973. nautobot/virtualization/templates/virtualization/virtualmachine.html +13 -2
  974. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
  975. nautobot/virtualization/tests/test_api.py +42 -52
  976. nautobot/virtualization/tests/test_filters.py +98 -75
  977. nautobot/virtualization/tests/test_models.py +36 -13
  978. nautobot/virtualization/tests/test_views.py +68 -73
  979. nautobot/virtualization/urls.py +10 -10
  980. nautobot/virtualization/views.py +8 -14
  981. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +15 -22
  982. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +987 -834
  983. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
  984. nautobot/circuits/api/nested_serializers.py +0 -69
  985. nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
  986. nautobot/dcim/api/nested_serializers.py +0 -356
  987. nautobot/dcim/templates/dcim/device_import.html +0 -5
  988. nautobot/dcim/templates/dcim/device_import_child.html +0 -5
  989. nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
  990. nautobot/extras/api/nested_serializers.py +0 -353
  991. nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -42
  992. nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -47
  993. nautobot/extras/reports.py +0 -60
  994. nautobot/extras/scripts.py +0 -72
  995. nautobot/extras/tests/example_jobs/script_variables.py +0 -67
  996. nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
  997. nautobot/extras/tests/example_jobs/test_fail.py +0 -16
  998. nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
  999. nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
  1000. nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
  1001. nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
  1002. nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
  1003. nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
  1004. nautobot/extras/tests/example_jobs/test_modify_db.py +0 -19
  1005. nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
  1006. nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
  1007. nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
  1008. nautobot/extras/tests/example_jobs/test_pass.py +0 -19
  1009. nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
  1010. nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
  1011. nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
  1012. nautobot/ipam/api/nested_serializers.py +0 -143
  1013. nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js +0 -29
  1014. nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js.map +0 -8
  1015. nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js +0 -18
  1016. nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js.map +0 -8
  1017. nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css +0 -1
  1018. nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css.map +0 -1
  1019. nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css +0 -1
  1020. nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css.map +0 -1
  1021. nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css +0 -1
  1022. nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css.map +0 -1
  1023. nautobot/tenancy/api/nested_serializers.py +0 -31
  1024. nautobot/users/api/nested_serializers.py +0 -67
  1025. nautobot/virtualization/api/nested_serializers.py +0 -65
  1026. /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
  1027. /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
  1028. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
  1029. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
@@ -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
@@ -50,7 +51,7 @@ from nautobot.dcim.models import (
50
51
  VirtualChassis,
51
52
  )
52
53
  from nautobot.extras.models import ConfigContextSchema, Role, SecretsGroup, Status
53
- from nautobot.ipam.models import IPAddress, VLAN
54
+ from nautobot.ipam.models import IPAddress, VLAN, Namespace, Prefix
54
55
  from nautobot.tenancy.models import Tenant
55
56
  from nautobot.virtualization.models import Cluster, ClusterType
56
57
 
@@ -61,7 +62,6 @@ User = get_user_model()
61
62
 
62
63
  class AppTest(APITestCase):
63
64
  def test_root(self):
64
-
65
65
  url = reverse("dcim-api:api-root")
66
66
  response = self.client.get(f"{url}?format=api", **self.header)
67
67
 
@@ -83,12 +83,20 @@ class Mixins:
83
83
  location=Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first(),
84
84
  device_type=DeviceType.objects.first(),
85
85
  role=Role.objects.get_for_model(Device).first(),
86
+ status=Status.objects.get_for_model(Device).first(),
86
87
  name="Peer Device",
87
88
  )
88
89
  if self.peer_termination_type is None:
89
90
  raise NotImplementedError("Test case must set peer_termination_type")
90
- peer_obj = self.peer_termination_type.objects.create(device=peer_device, name="Peer Termination")
91
- 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)
92
100
  cable.save()
93
101
 
94
102
  self.add_permissions(f"dcim.view_{self.model._meta.model_name}")
@@ -106,7 +114,6 @@ class Mixins:
106
114
  """Mixin class for all `ComponentModel` model class tests."""
107
115
 
108
116
  model = None
109
- brief_fields = ["device", "display", "id", "name", "url"]
110
117
  bulk_update_data = {
111
118
  "description": "New description",
112
119
  }
@@ -119,25 +126,26 @@ class Mixins:
119
126
  cls.manufacturer = cls.device_type.manufacturer
120
127
  cls.location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
121
128
  cls.device_role = Role.objects.get_for_model(Device).first()
129
+ cls.device_status = Status.objects.get_for_model(Device).first()
122
130
  cls.device = Device.objects.create(
123
- 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,
124
136
  )
125
137
 
126
138
  class BasePortTestMixin(ComponentTraceMixin, BaseComponentTestMixin):
127
139
  """Mixin class for all `FooPort` tests."""
128
140
 
129
141
  peer_termination_type = None
130
- brief_fields = ["cable", "device", "display", "id", "name", "url"]
131
142
 
132
143
  class BasePortTemplateTestMixin(BaseComponentTestMixin):
133
144
  """Mixin class for all `FooPortTemplate` tests."""
134
145
 
135
- brief_fields = ["display", "id", "name", "url"]
136
-
137
146
 
138
- class LocationTypeTest(APIViewTestCases.APIViewTestCase):
147
+ class LocationTypeTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelAPIViewTestCaseMixin):
139
148
  model = LocationType
140
- brief_fields = ["display", "id", "name", "slug", "tree_depth", "url"]
141
149
  bulk_update_data = {
142
150
  "description": "Some generic description of multiple types. Not very useful.",
143
151
  "nestable": True,
@@ -178,9 +186,8 @@ class LocationTypeTest(APIViewTestCases.APIViewTestCase):
178
186
  ]
179
187
 
180
188
 
181
- class LocationTest(APIViewTestCases.APIViewTestCase):
189
+ class LocationTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelAPIViewTestCaseMixin):
182
190
  model = Location
183
- brief_fields = ["display", "id", "name", "slug", "tree_depth", "url"]
184
191
  choices_fields = []
185
192
  slug_source = ["parent__slug", "name"]
186
193
 
@@ -191,18 +198,18 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
191
198
  cls.lt3 = LocationType.objects.get(name="Floor")
192
199
  cls.lt4 = LocationType.objects.get(name="Room")
193
200
 
194
- cls.status_active = Status.objects.get(slug="active")
195
- tenant = Tenant.objects.create(name="Test Tenant")
201
+ cls.location_statuses = Status.objects.get_for_model(Location)
202
+ tenant = Tenant.objects.first()
196
203
 
197
- cls.loc1 = Location.objects.create(name="RTP", location_type=cls.lt1, status=cls.status_active)
204
+ cls.loc1 = Location.objects.create(name="RTP", location_type=cls.lt1, status=cls.location_statuses[0])
198
205
  cls.loc2 = Location.objects.create(
199
- name="RTP4E", location_type=cls.lt2, status=cls.status_active, parent=cls.loc1
206
+ name="RTP4E", location_type=cls.lt2, status=cls.location_statuses[0], parent=cls.loc1
200
207
  )
201
208
  cls.loc3 = Location.objects.create(
202
- name="RTP4E-3", location_type=cls.lt3, status=cls.status_active, parent=cls.loc2
209
+ name="RTP4E-3", location_type=cls.lt3, status=cls.location_statuses[0], parent=cls.loc2
203
210
  )
204
211
  cls.loc4 = Location.objects.create(
205
- name="RTP4E-3-0101", location_type=cls.lt4, status=cls.status_active, parent=cls.loc3, tenant=tenant
212
+ name="RTP4E-3-0101", location_type=cls.lt4, status=cls.location_statuses[1], parent=cls.loc3, tenant=tenant
206
213
  )
207
214
  for loc in [cls.loc1, cls.loc2, cls.loc3, cls.loc4]:
208
215
  loc.validated_save()
@@ -211,35 +218,33 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
211
218
  {
212
219
  "name": "Downtown Durham",
213
220
  "location_type": cls.lt1.pk,
214
- "status": cls.status_active.pk,
221
+ "status": cls.location_statuses[0].pk,
215
222
  },
216
223
  {
217
224
  "name": "RTP12",
218
225
  "slug": "rtp-12",
219
226
  "location_type": cls.lt2.pk,
220
227
  "parent": cls.loc1.pk,
221
- "status": cls.status_active.pk,
228
+ "status": cls.location_statuses[0].pk,
222
229
  },
223
230
  {
224
231
  "name": "RTP4E-2",
225
232
  "location_type": cls.lt3.pk,
226
233
  "parent": cls.loc2.pk,
227
- "status": cls.status_active.pk,
234
+ "status": cls.location_statuses[0].pk,
228
235
  "description": "Second floor of RTP4E",
229
236
  "tenant": tenant.pk,
230
237
  },
231
238
  ]
232
239
 
233
- status_planned = Status.objects.get(slug="planned")
234
-
235
240
  # Changing location_type of an existing instance is not permitted
236
241
  cls.update_data = {
237
242
  "name": "A revised location",
238
243
  "slug": "a-different-slug",
239
- "status": status_planned.pk,
244
+ "status": cls.location_statuses[1].pk,
240
245
  }
241
246
  cls.bulk_update_data = {
242
- "status": status_planned.pk,
247
+ "status": cls.location_statuses[1].pk,
243
248
  }
244
249
 
245
250
  def test_time_zone_field_post_null(self):
@@ -252,10 +257,9 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
252
257
  location = {
253
258
  "name": "foo",
254
259
  "slug": "foo",
255
- "status": self.status_active.pk,
260
+ "status": self.location_statuses[0].pk,
256
261
  "time_zone": None,
257
262
  "location_type": self.lt1.pk,
258
- "location": self.loc1.pk,
259
263
  }
260
264
 
261
265
  # Attempt to create new location with null time_zone attr.
@@ -273,10 +277,9 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
273
277
  location = {
274
278
  "name": "foo",
275
279
  "slug": "foo",
276
- "status": self.status_active.pk,
280
+ "status": self.location_statuses[0].pk,
277
281
  "time_zone": "",
278
282
  "location_type": self.lt1.pk,
279
- "location": self.loc1.pk,
280
283
  }
281
284
 
282
285
  # Attempt to create new location with blank time_zone attr.
@@ -295,10 +298,9 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
295
298
  location = {
296
299
  "name": "foo",
297
300
  "slug": "foo",
298
- "status": self.status_active.pk,
301
+ "status": self.location_statuses[0].pk,
299
302
  "time_zone": time_zone,
300
303
  "location_type": self.lt1.pk,
301
- "location": self.loc1.pk,
302
304
  }
303
305
 
304
306
  # Attempt to create new location with valid time_zone attr.
@@ -317,10 +319,9 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
317
319
  location = {
318
320
  "name": "foo",
319
321
  "slug": "foo",
320
- "status": self.status_active.pk,
322
+ "status": self.location_statuses[0].pk,
321
323
  "time_zone": time_zone,
322
324
  "location_type": self.lt1.pk,
323
- "location": self.loc1.pk,
324
325
  }
325
326
 
326
327
  # Attempt to create new location with invalid time_zone attr.
@@ -344,9 +345,8 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
344
345
  self.assertEqual(response.json()["time_zone"], None)
345
346
 
346
347
 
347
- class RackGroupTest(APIViewTestCases.APIViewTestCase):
348
+ class RackGroupTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelAPIViewTestCaseMixin):
348
349
  model = RackGroup
349
- brief_fields = ["display", "id", "name", "rack_count", "slug", "tree_depth", "url"]
350
350
  bulk_update_data = {
351
351
  "description": "New description",
352
352
  }
@@ -354,11 +354,11 @@ class RackGroupTest(APIViewTestCases.APIViewTestCase):
354
354
 
355
355
  @classmethod
356
356
  def setUpTestData(cls):
357
- cls.active = Status.objects.get(slug="active")
357
+ cls.status = Status.objects.get_for_model(Location).first()
358
358
  location_type = LocationType.objects.create(name="Location Type 1")
359
359
  cls.locations = (
360
- Location.objects.create(name="Location 1", location_type=location_type, status=cls.active),
361
- Location.objects.create(name="Location 2", location_type=location_type, status=cls.active),
360
+ Location.objects.create(name="Location 1", location_type=location_type, status=cls.status),
361
+ Location.objects.create(name="Location 2", location_type=location_type, status=cls.status),
362
362
  )
363
363
  cls.parent_rack_groups = (
364
364
  RackGroup.objects.create(location=cls.locations[0], name="Parent Rack Group 1", slug="parent-rack-group-1"),
@@ -423,7 +423,7 @@ class RackGroupTest(APIViewTestCases.APIViewTestCase):
423
423
  )
424
424
  child_location_type.content_types.add(ContentType.objects.get_for_model(RackGroup))
425
425
  child_location = Location.objects.create(
426
- name="Child Location", location_type=child_location_type, parent=self.locations[0], status=self.active
426
+ name="Child Location", location_type=child_location_type, parent=self.locations[0], status=self.status
427
427
  )
428
428
 
429
429
  data = {
@@ -442,7 +442,7 @@ class RackGroupTest(APIViewTestCases.APIViewTestCase):
442
442
  parent_group = RackGroup.objects.filter(location=self.locations[0]).first()
443
443
  # A sibling of locations[0], not a child of it.
444
444
  sibling_location = Location.objects.create(
445
- name="Location 1B", location_type=self.locations[0].location_type, status=self.active
445
+ name="Location 1B", location_type=self.locations[0].location_type, status=self.status
446
446
  )
447
447
 
448
448
  data = {
@@ -462,12 +462,10 @@ class RackGroupTest(APIViewTestCases.APIViewTestCase):
462
462
 
463
463
  class RackTest(APIViewTestCases.APIViewTestCase):
464
464
  model = Rack
465
- brief_fields = ["device_count", "display", "id", "name", "url"]
466
465
  choices_fields = ["outer_unit", "type", "width"]
467
466
 
468
467
  @classmethod
469
468
  def setUpTestData(cls):
470
-
471
469
  locations = Location.objects.all()[:2]
472
470
 
473
471
  rack_groups = (
@@ -556,7 +554,7 @@ class RackTest(APIViewTestCases.APIViewTestCase):
556
554
  rack = Rack.objects.first()
557
555
  self.add_permissions("dcim.view_rack")
558
556
  url = reverse("dcim-api:rack-elevation", kwargs={"pk": rack.pk})
559
- params = {"brief": "true", "face": "front", "exclude": "a85a31aa-094f-4de9-8ba6-16cb088a1b74"}
557
+ params = {"face": "front", "exclude": "a85a31aa-094f-4de9-8ba6-16cb088a1b74"}
560
558
  response = self.client.get(url, params, **self.header)
561
559
  self.assertHttpStatus(response, 200)
562
560
 
@@ -608,7 +606,6 @@ class RackTest(APIViewTestCases.APIViewTestCase):
608
606
 
609
607
  class RackReservationTest(APIViewTestCases.APIViewTestCase):
610
608
  model = RackReservation
611
- brief_fields = ["display", "id", "units", "url", "user"]
612
609
  bulk_update_data = {
613
610
  "description": "New description",
614
611
  }
@@ -617,10 +614,11 @@ class RackReservationTest(APIViewTestCases.APIViewTestCase):
617
614
  def setUpTestData(cls):
618
615
  user = User.objects.create(username="user1", is_active=True)
619
616
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
617
+ rack_status = Status.objects.get_for_model(Rack).first()
620
618
 
621
619
  cls.racks = (
622
- Rack.objects.create(location=location, name="Rack 1"),
623
- 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),
624
622
  )
625
623
 
626
624
  RackReservation.objects.create(rack=cls.racks[0], units=[1, 2, 3], user=user, description="Reservation #1")
@@ -655,19 +653,15 @@ class RackReservationTest(APIViewTestCases.APIViewTestCase):
655
653
 
656
654
  class ManufacturerTest(APIViewTestCases.APIViewTestCase):
657
655
  model = Manufacturer
658
- brief_fields = ["device_type_count", "display", "id", "name", "slug", "url"]
659
656
  create_data = [
660
657
  {
661
658
  "name": "Test Manufacturer 4",
662
- "slug": "test-manufacturer-4",
663
659
  },
664
660
  {
665
661
  "name": "Test Manufacturer 5",
666
- "slug": "test-manufacturer-5",
667
662
  },
668
663
  {
669
664
  "name": "Test Manufacturer 6",
670
- "slug": "test-manufacturer-6",
671
665
  },
672
666
  {
673
667
  "name": "Test Manufacturer 7",
@@ -676,11 +670,9 @@ class ManufacturerTest(APIViewTestCases.APIViewTestCase):
676
670
  bulk_update_data = {
677
671
  "description": "New description",
678
672
  }
679
- slug_source = "name"
680
673
 
681
674
  @classmethod
682
675
  def setUpTestData(cls):
683
-
684
676
  # FIXME(jathan): This has to be replaced with# `get_deletable_object` and
685
677
  # `get_deletable_object_pks` but this is a workaround just so all of these objects are
686
678
  # deletable for now.
@@ -690,15 +682,6 @@ class ManufacturerTest(APIViewTestCases.APIViewTestCase):
690
682
 
691
683
  class DeviceTypeTest(APIViewTestCases.APIViewTestCase):
692
684
  model = DeviceType
693
- brief_fields = [
694
- "device_count",
695
- "display",
696
- "id",
697
- "manufacturer",
698
- "model",
699
- "slug",
700
- "url",
701
- ]
702
685
  bulk_update_data = {
703
686
  "part_number": "ABC123",
704
687
  }
@@ -1028,19 +1011,15 @@ class DeviceBayTemplateTest(Mixins.BasePortTemplateTestMixin):
1028
1011
 
1029
1012
  class PlatformTest(APIViewTestCases.APIViewTestCase):
1030
1013
  model = Platform
1031
- brief_fields = ["device_count", "display", "id", "name", "slug", "url", "virtual_machine_count"]
1032
1014
  create_data = [
1033
1015
  {
1034
1016
  "name": "Test Platform 4",
1035
- "slug": "test-platform-4",
1036
1017
  },
1037
1018
  {
1038
1019
  "name": "Test Platform 5",
1039
- "slug": "test-platform-5",
1040
1020
  },
1041
1021
  {
1042
1022
  "name": "Test Platform 6",
1043
- "slug": "test-platform-6",
1044
1023
  },
1045
1024
  {
1046
1025
  "name": "Test Platform 7",
@@ -1049,27 +1028,25 @@ class PlatformTest(APIViewTestCases.APIViewTestCase):
1049
1028
  bulk_update_data = {
1050
1029
  "description": "New description",
1051
1030
  }
1052
- slug_source = "name"
1053
1031
 
1054
1032
 
1055
1033
  class DeviceTest(APIViewTestCases.APIViewTestCase):
1056
1034
  model = Device
1057
- brief_fields = ["display", "id", "name", "url"]
1058
1035
  choices_fields = ["face"]
1059
1036
 
1060
1037
  @classmethod
1061
1038
  def setUpTestData(cls):
1062
-
1063
1039
  locations = Location.objects.filter(location_type=LocationType.objects.get(name="Campus"))[:2]
1064
1040
 
1041
+ rack_status = Status.objects.get_for_model(Rack).first()
1065
1042
  racks = (
1066
- Rack.objects.create(name="Rack 1", location=locations[0]),
1067
- 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),
1068
1045
  )
1069
1046
 
1070
1047
  device_statuses = Status.objects.get_for_model(Device)
1071
1048
 
1072
- cluster_type = ClusterType.objects.create(name="Cluster Type 1", slug="cluster-type-1")
1049
+ cluster_type = ClusterType.objects.create(name="Cluster Type 1")
1073
1050
 
1074
1051
  clusters = (
1075
1052
  Cluster.objects.create(name="Cluster 1", cluster_type=cluster_type),
@@ -1077,8 +1054,8 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1077
1054
  )
1078
1055
 
1079
1056
  secrets_groups = (
1080
- SecretsGroup.objects.create(name="Secrets Group 1", slug="secrets-group-1"),
1081
- SecretsGroup.objects.create(name="Secrets Group 2", slug="secrets-group-2"),
1057
+ SecretsGroup.objects.create(name="Secrets Group 1"),
1058
+ SecretsGroup.objects.create(name="Secrets Group 2"),
1082
1059
  )
1083
1060
 
1084
1061
  device_type = DeviceType.objects.first()
@@ -1154,27 +1131,28 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1154
1131
  "status": device_statuses[1].pk,
1155
1132
  }
1156
1133
 
1157
- def test_config_context_included_by_default_in_list_view(self):
1134
+ def test_config_context_excluded_by_default_in_list_view(self):
1158
1135
  """
1159
- 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.
1160
1137
  """
1161
1138
  self.add_permissions("dcim.view_device")
1162
1139
  url = reverse("dcim-api:device-list")
1163
1140
  response = self.client.get(url, **self.header)
1164
1141
 
1165
1142
  self.assertHttpStatus(response, status.HTTP_200_OK)
1166
- self.assertEqual(response.data["results"][0].get("config_context", {}).get("A"), 1)
1143
+ self.assertNotIn("config_context", response.data["results"][0])
1167
1144
 
1168
- def test_config_context_excluded(self):
1145
+ def test_config_context_included(self):
1169
1146
  """
1170
- 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.
1171
1148
  """
1172
1149
  self.add_permissions("dcim.view_device")
1173
- url = reverse("dcim-api:device-list") + "?exclude=config_context"
1150
+ url = reverse("dcim-api:device-list") + "?include=config_context"
1174
1151
  response = self.client.get(url, **self.header)
1175
1152
 
1176
1153
  self.assertHttpStatus(response, status.HTTP_200_OK)
1177
- 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})
1178
1156
 
1179
1157
  def test_unique_name_per_location_constraint(self):
1180
1158
  """
@@ -1211,7 +1189,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1211
1189
  self._get_detail_url(Device.objects.get(name="Device 1")), patch_data, format="json", **self.header
1212
1190
  )
1213
1191
  self.assertHttpStatus(response, status.HTTP_200_OK)
1214
- 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))
1215
1193
 
1216
1194
  def test_local_config_context_schema_schema_validation_fails(self):
1217
1195
  """
@@ -1240,8 +1218,13 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1240
1218
  self.add_permissions("dcim.change_device")
1241
1219
 
1242
1220
  dev = Device.objects.get(name="Device 3")
1243
- dev_intf = Interface.objects.create(name="Ethernet1", device=dev, type="1000base-t")
1244
- dev_ip_addr = IPAddress.objects.create(address="192.0.2.1/24")
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()
1225
+ namespace = Namespace.objects.first()
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)
1245
1228
  dev_intf.add_ip_addresses(dev_ip_addr)
1246
1229
 
1247
1230
  patch_data = {"primary_ip4": dev_ip_addr.pk}
@@ -1417,17 +1400,23 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1417
1400
  @classmethod
1418
1401
  def setUpTestData(cls):
1419
1402
  super().setUpTestData()
1420
- status_active = Status.objects.get(slug="active")
1403
+ interface_status = Status.objects.get_for_model(Interface).first()
1421
1404
 
1422
1405
  cls.devices = (
1406
+ cls.device,
1423
1407
  Device.objects.create(
1424
- device_type=cls.device_type, role=cls.device_role, name="Device 1", location=cls.location
1425
- ),
1426
- Device.objects.create(
1427
- 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,
1428
1413
  ),
1429
1414
  Device.objects.create(
1430
- 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,
1431
1420
  ),
1432
1421
  )
1433
1422
 
@@ -1437,24 +1426,58 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1437
1426
  Device.objects.filter(id=cls.devices[0].id).update(virtual_chassis=cls.virtual_chassis, vc_position=1)
1438
1427
  Device.objects.filter(id=cls.devices[1].id).update(virtual_chassis=cls.virtual_chassis, vc_position=2)
1439
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()
1440
1431
  cls.interfaces = (
1441
- Interface.objects.create(device=cls.devices[0], name="Interface 1", type="1000base-t"),
1442
- Interface.objects.create(device=cls.devices[0], name="Interface 2", type="1000base-t"),
1443
- Interface.objects.create(device=cls.devices[0], name="Interface 3", type=InterfaceTypeChoices.TYPE_BRIDGE),
1444
1432
  Interface.objects.create(
1445
- 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,
1455
+ ),
1456
+ Interface.objects.create(
1457
+ device=cls.devices[1],
1458
+ name="Interface 5",
1459
+ type=InterfaceTypeChoices.TYPE_LAG,
1460
+ status=non_default_status,
1446
1461
  ),
1447
- Interface.objects.create(device=cls.devices[1], name="Interface 5", type=InterfaceTypeChoices.TYPE_LAG),
1448
- Interface.objects.create(device=cls.devices[2], name="Interface 6", type=InterfaceTypeChoices.TYPE_LAG),
1449
1462
  Interface.objects.create(
1450
- device=cls.devices[2], name="Interface 7", type=InterfaceTypeChoices.TYPE_1GE_GBIC
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,
1451
1473
  ),
1452
1474
  )
1453
1475
 
1476
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1454
1477
  cls.vlans = (
1455
- VLAN.objects.create(name="VLAN 1", vid=1),
1456
- VLAN.objects.create(name="VLAN 2", vid=2),
1457
- 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),
1458
1481
  )
1459
1482
 
1460
1483
  cls.create_data = [
@@ -1462,16 +1485,17 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1462
1485
  "device": cls.devices[0].pk,
1463
1486
  "name": "Interface 8",
1464
1487
  "type": "1000base-t",
1465
- "status": status_active.pk,
1488
+ "status": interface_status.pk,
1466
1489
  "mode": InterfaceModeChoices.MODE_TAGGED,
1467
1490
  "tagged_vlans": [cls.vlans[0].pk, cls.vlans[1].pk],
1468
1491
  "untagged_vlan": cls.vlans[2].pk,
1492
+ "mac_address": "00-01-02-03-04-05",
1469
1493
  },
1470
1494
  {
1471
1495
  "device": cls.devices[0].pk,
1472
1496
  "name": "Interface 9",
1473
1497
  "type": "1000base-t",
1474
- "status": status_active.pk,
1498
+ "status": interface_status.pk,
1475
1499
  "mode": InterfaceModeChoices.MODE_TAGGED,
1476
1500
  "bridge": cls.interfaces[3].pk,
1477
1501
  "tagged_vlans": [cls.vlans[0].pk, cls.vlans[1].pk],
@@ -1481,7 +1505,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1481
1505
  "device": cls.devices[0].pk,
1482
1506
  "name": "Interface 10",
1483
1507
  "type": "virtual",
1484
- "status": status_active.pk,
1508
+ "status": interface_status.pk,
1485
1509
  "mode": InterfaceModeChoices.MODE_TAGGED,
1486
1510
  "parent_interface": cls.interfaces[1].pk,
1487
1511
  "tagged_vlans": [cls.vlans[0].pk, cls.vlans[1].pk],
@@ -1493,7 +1517,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1493
1517
  "device": cls.devices[0].pk,
1494
1518
  "name": "expected-to-fail",
1495
1519
  "type": InterfaceTypeChoices.TYPE_VIRTUAL,
1496
- "status": status_active.pk,
1520
+ "status": interface_status.pk,
1497
1521
  "untagged_vlan": cls.vlans[0].pk,
1498
1522
  }
1499
1523
 
@@ -1502,7 +1526,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1502
1526
  "device": cls.devices[0].pk,
1503
1527
  "name": "interface test 1",
1504
1528
  "type": InterfaceTypeChoices.TYPE_VIRTUAL,
1505
- "status": status_active.pk,
1529
+ "status": interface_status.pk,
1506
1530
  "parent_interface": cls.interfaces[3].id, # belongs to different device but same vc
1507
1531
  "bridge": cls.interfaces[2].id, # belongs to different device but same vc
1508
1532
  },
@@ -1510,7 +1534,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1510
1534
  "device": cls.devices[0].pk,
1511
1535
  "name": "interface test 2",
1512
1536
  "type": InterfaceTypeChoices.TYPE_1GE_GBIC,
1513
- "status": status_active.pk,
1537
+ "status": interface_status.pk,
1514
1538
  "lag": cls.interfaces[4].id, # belongs to different device but same vc
1515
1539
  },
1516
1540
  ]
@@ -1522,7 +1546,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1522
1546
  "device": cls.devices[0].pk,
1523
1547
  "name": "interface test 1",
1524
1548
  "type": InterfaceTypeChoices.TYPE_VIRTUAL,
1525
- "status": status_active.pk,
1549
+ "status": interface_status.pk,
1526
1550
  "parent_interface": cls.interfaces[6].id, # do not belong to same device or vc
1527
1551
  },
1528
1552
  ],
@@ -1532,7 +1556,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1532
1556
  "device": cls.devices[0].pk,
1533
1557
  "name": "interface test 2",
1534
1558
  "type": InterfaceTypeChoices.TYPE_1GE_GBIC,
1535
- "status": status_active.pk,
1559
+ "status": interface_status.pk,
1536
1560
  "bridge": cls.interfaces[6].id, # does not belong to same device or vc
1537
1561
  },
1538
1562
  ],
@@ -1542,7 +1566,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1542
1566
  "device": cls.devices[0].pk,
1543
1567
  "name": "interface test 3",
1544
1568
  "type": InterfaceTypeChoices.TYPE_1GE_GBIC,
1545
- "status": status_active.pk,
1569
+ "status": interface_status.pk,
1546
1570
  "lag": cls.interfaces[6].id, # does not belong to same device or vc
1547
1571
  },
1548
1572
  ],
@@ -1632,6 +1656,7 @@ class InterfaceTest(Mixins.BasePortTestMixin):
1632
1656
  name="Tagged Interface",
1633
1657
  mode=InterfaceModeChoices.MODE_TAGGED,
1634
1658
  type=InterfaceTypeChoices.TYPE_VIRTUAL,
1659
+ status=Status.objects.get_for_model(Interface).first(),
1635
1660
  )
1636
1661
  interface.tagged_vlans.add(self.vlans[0])
1637
1662
  payload = {"mode": None, "tagged_vlans": [self.vlans[2].pk]}
@@ -1772,28 +1797,33 @@ class DeviceBayTest(Mixins.BaseComponentTestMixin):
1772
1797
  )
1773
1798
 
1774
1799
  devices = (
1800
+ # "Device 1" was already created in super().setUpTestData
1775
1801
  Device.objects.create(
1776
1802
  device_type=device_types[0],
1777
1803
  role=cls.device_role,
1778
- name="Device 1",
1804
+ status=cls.device_status,
1805
+ name="Device 2",
1779
1806
  location=cls.location,
1780
1807
  ),
1781
1808
  Device.objects.create(
1782
1809
  device_type=device_types[1],
1783
1810
  role=cls.device_role,
1784
- name="Device 2",
1811
+ status=cls.device_status,
1812
+ name="Device 3",
1785
1813
  location=cls.location,
1786
1814
  ),
1787
1815
  Device.objects.create(
1788
1816
  device_type=device_types[1],
1789
1817
  role=cls.device_role,
1790
- name="Device 3",
1818
+ status=cls.device_status,
1819
+ name="Device 4",
1791
1820
  location=cls.location,
1792
1821
  ),
1793
1822
  Device.objects.create(
1794
1823
  device_type=device_types[1],
1795
1824
  role=cls.device_role,
1796
- name="Device 4",
1825
+ status=cls.device_status,
1826
+ name="Device 5",
1797
1827
  location=cls.location,
1798
1828
  ),
1799
1829
  )
@@ -1821,9 +1851,8 @@ class DeviceBayTest(Mixins.BaseComponentTestMixin):
1821
1851
  ]
1822
1852
 
1823
1853
 
1824
- class InventoryItemTest(Mixins.BaseComponentTestMixin):
1854
+ class InventoryItemTest(Mixins.BaseComponentTestMixin, APIViewTestCases.TreeModelAPIViewTestCaseMixin):
1825
1855
  model = InventoryItem
1826
- brief_fields = ["device", "display", "id", "name", "tree_depth", "url"]
1827
1856
  choices_fields = []
1828
1857
 
1829
1858
  @classmethod
@@ -1852,10 +1881,18 @@ class InventoryItemTest(Mixins.BaseComponentTestMixin):
1852
1881
  },
1853
1882
  ]
1854
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
+
1855
1893
 
1856
1894
  class CableTest(Mixins.BaseComponentTestMixin):
1857
1895
  model = Cable
1858
- brief_fields = ["display", "id", "label", "url"]
1859
1896
  bulk_update_data = {
1860
1897
  "length": 100,
1861
1898
  "length_unit": "m",
@@ -1872,18 +1909,21 @@ class CableTest(Mixins.BaseComponentTestMixin):
1872
1909
  Device.objects.create(
1873
1910
  device_type=cls.device_type,
1874
1911
  role=cls.device_role,
1912
+ status=cls.device_status,
1875
1913
  name="Device 2",
1876
1914
  location=cls.location,
1877
1915
  ),
1878
1916
  Device.objects.create(
1879
1917
  device_type=cls.device_type,
1880
1918
  role=cls.device_role,
1919
+ status=cls.device_status,
1881
1920
  name="Device 3",
1882
1921
  location=cls.location,
1883
1922
  ),
1884
1923
  )
1885
1924
 
1886
1925
  interfaces = []
1926
+ interface_status = Status.objects.get_for_model(Interface).first()
1887
1927
  for device in devices:
1888
1928
  for i in range(0, 10):
1889
1929
  interfaces.append(
@@ -1891,6 +1931,7 @@ class CableTest(Mixins.BaseComponentTestMixin):
1891
1931
  device=device,
1892
1932
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
1893
1933
  name=f"eth{i}",
1934
+ status=interface_status,
1894
1935
  )
1895
1936
  )
1896
1937
 
@@ -1945,29 +1986,32 @@ class CableTest(Mixins.BaseComponentTestMixin):
1945
1986
 
1946
1987
  class ConnectedDeviceTest(APITestCase):
1947
1988
  def setUp(self):
1948
-
1949
1989
  super().setUp()
1950
1990
 
1951
1991
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1952
1992
  device_type = DeviceType.objects.exclude(manufacturer__isnull=True).first()
1953
1993
  device_role = Role.objects.get_for_model(Device).first()
1994
+ device_status = Status.objects.get_for_model(Device).first()
1954
1995
 
1955
- cable_status = Status.objects.get_for_model(Cable).get(slug="connected")
1996
+ cable_status = Status.objects.get_for_model(Cable).get(name="Connected")
1956
1997
 
1957
1998
  self.device1 = Device.objects.create(
1958
1999
  device_type=device_type,
1959
2000
  role=device_role,
2001
+ status=device_status,
1960
2002
  name="TestDevice1",
1961
2003
  location=location,
1962
2004
  )
1963
2005
  device2 = Device.objects.create(
1964
2006
  device_type=device_type,
1965
2007
  role=device_role,
2008
+ status=device_status,
1966
2009
  name="TestDevice2",
1967
2010
  location=location,
1968
2011
  )
1969
- interface1 = Interface.objects.create(device=self.device1, name="eth0")
1970
- 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)
1971
2015
 
1972
2016
  cable = Cable(termination_a=interface1, termination_b=interface2, status=cable_status)
1973
2017
  cable.validated_save()
@@ -1982,90 +2026,103 @@ class ConnectedDeviceTest(APITestCase):
1982
2026
 
1983
2027
  class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
1984
2028
  model = VirtualChassis
1985
- brief_fields = ["display", "id", "master", "member_count", "name", "url"]
1986
2029
 
1987
2030
  @classmethod
1988
2031
  def setUpTestData(cls):
1989
2032
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1990
2033
  device_type = DeviceType.objects.exclude(manufacturer__isnull=True).first()
1991
2034
  device_role = Role.objects.get_for_model(Device).first()
2035
+ device_status = Status.objects.get_for_model(Device).first()
1992
2036
 
1993
2037
  devices = (
1994
2038
  Device.objects.create(
1995
2039
  name="Device 1",
1996
2040
  device_type=device_type,
1997
2041
  role=device_role,
2042
+ status=device_status,
1998
2043
  location=location,
1999
2044
  ),
2000
2045
  Device.objects.create(
2001
2046
  name="Device 2",
2002
2047
  device_type=device_type,
2003
2048
  role=device_role,
2049
+ status=device_status,
2004
2050
  location=location,
2005
2051
  ),
2006
2052
  Device.objects.create(
2007
2053
  name="Device 3",
2008
2054
  device_type=device_type,
2009
2055
  role=device_role,
2056
+ status=device_status,
2010
2057
  location=location,
2011
2058
  ),
2012
2059
  Device.objects.create(
2013
2060
  name="Device 4",
2014
2061
  device_type=device_type,
2015
2062
  role=device_role,
2063
+ status=device_status,
2016
2064
  location=location,
2017
2065
  ),
2018
2066
  Device.objects.create(
2019
2067
  name="Device 5",
2020
2068
  device_type=device_type,
2021
2069
  role=device_role,
2070
+ status=device_status,
2022
2071
  location=location,
2023
2072
  ),
2024
2073
  Device.objects.create(
2025
2074
  name="Device 6",
2026
2075
  device_type=device_type,
2027
2076
  role=device_role,
2077
+ status=device_status,
2028
2078
  location=location,
2029
2079
  ),
2030
2080
  Device.objects.create(
2031
2081
  name="Device 7",
2032
2082
  device_type=device_type,
2033
2083
  role=device_role,
2084
+ status=device_status,
2034
2085
  location=location,
2035
2086
  ),
2036
2087
  Device.objects.create(
2037
2088
  name="Device 8",
2038
2089
  device_type=device_type,
2039
2090
  role=device_role,
2091
+ status=device_status,
2040
2092
  location=location,
2041
2093
  ),
2042
2094
  Device.objects.create(
2043
2095
  name="Device 9",
2044
2096
  device_type=device_type,
2045
2097
  role=device_role,
2098
+ status=device_status,
2046
2099
  location=location,
2047
2100
  ),
2048
2101
  Device.objects.create(
2049
2102
  name="Device 10",
2050
2103
  device_type=device_type,
2051
2104
  role=device_role,
2105
+ status=device_status,
2052
2106
  location=location,
2053
2107
  ),
2054
2108
  Device.objects.create(
2055
2109
  name="Device 11",
2056
2110
  device_type=device_type,
2057
2111
  role=device_role,
2112
+ status=device_status,
2058
2113
  location=location,
2059
2114
  ),
2060
2115
  Device.objects.create(
2061
2116
  name="Device 12",
2062
2117
  device_type=device_type,
2063
2118
  role=device_role,
2119
+ status=device_status,
2064
2120
  location=location,
2065
2121
  ),
2066
2122
  )
2067
2123
 
2068
2124
  # Create 12 interfaces per device
2125
+ interface_status = Status.objects.get_for_model(Interface).first()
2069
2126
  interfaces = []
2070
2127
  for i, device in enumerate(devices):
2071
2128
  for j in range(0, 13):
@@ -2075,6 +2132,7 @@ class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
2075
2132
  device=device,
2076
2133
  name=f"{i%3+1}/{j}",
2077
2134
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2135
+ status=interface_status,
2078
2136
  )
2079
2137
  )
2080
2138
 
@@ -2150,9 +2208,10 @@ class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
2150
2208
  virtual_chassis_1 = response.json()["results"][0]
2151
2209
 
2152
2210
  # Make sure the master is set
2153
- self.assertNotEqual(virtual_chassis_1["master"], None)
2211
+ self.assertIsNotNone(virtual_chassis_1["master"])
2154
2212
 
2155
- 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])
2156
2215
 
2157
2216
  # Set the virtual_chassis of the master device to null
2158
2217
  url = reverse("dcim-api:device-detail", kwargs={"pk": master_device.id})
@@ -2172,7 +2231,6 @@ class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
2172
2231
 
2173
2232
  class PowerPanelTest(APIViewTestCases.APIViewTestCase):
2174
2233
  model = PowerPanel
2175
- brief_fields = ["display", "id", "name", "power_feed_count", "url"]
2176
2234
 
2177
2235
  @classmethod
2178
2236
  def setUpTestData(cls):
@@ -2211,7 +2269,6 @@ class PowerPanelTest(APIViewTestCases.APIViewTestCase):
2211
2269
 
2212
2270
  class PowerFeedTest(APIViewTestCases.APIViewTestCase):
2213
2271
  model = PowerFeed
2214
- brief_fields = ["cable", "display", "id", "name", "url"]
2215
2272
  choices_fields = ["phase", "supply", "type"]
2216
2273
 
2217
2274
  @classmethod
@@ -2219,12 +2276,21 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
2219
2276
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
2220
2277
  rackgroup = RackGroup.objects.create(location=location, name="Rack Group 1", slug="rack-group-1")
2221
2278
  rackrole = Role.objects.get_for_model(Rack).first()
2279
+ rackstatus = Status.objects.get_for_model(Rack).first()
2222
2280
 
2223
2281
  racks = (
2224
- Rack.objects.create(location=location, rack_group=rackgroup, role=rackrole, name="Rack 1"),
2225
- Rack.objects.create(location=location, rack_group=rackgroup, role=rackrole, name="Rack 2"),
2226
- Rack.objects.create(location=location, rack_group=rackgroup, role=rackrole, name="Rack 3"),
2227
- 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
+ ),
2228
2294
  )
2229
2295
 
2230
2296
  power_panels = (
@@ -2234,40 +2300,47 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
2234
2300
 
2235
2301
  PRIMARY = PowerFeedTypeChoices.TYPE_PRIMARY
2236
2302
  REDUNDANT = PowerFeedTypeChoices.TYPE_REDUNDANT
2303
+ pf_status = Status.objects.get_for_model(PowerFeed).first()
2237
2304
  PowerFeed.objects.create(
2238
2305
  power_panel=power_panels[0],
2239
2306
  rack=racks[0],
2240
2307
  name="Power Feed 1A",
2308
+ status=pf_status,
2241
2309
  type=PRIMARY,
2242
2310
  )
2243
2311
  PowerFeed.objects.create(
2244
2312
  power_panel=power_panels[1],
2245
2313
  rack=racks[0],
2246
2314
  name="Power Feed 1B",
2315
+ status=pf_status,
2247
2316
  type=REDUNDANT,
2248
2317
  )
2249
2318
  PowerFeed.objects.create(
2250
2319
  power_panel=power_panels[0],
2251
2320
  rack=racks[1],
2252
2321
  name="Power Feed 2A",
2322
+ status=pf_status,
2253
2323
  type=PRIMARY,
2254
2324
  )
2255
2325
  PowerFeed.objects.create(
2256
2326
  power_panel=power_panels[1],
2257
2327
  rack=racks[1],
2258
2328
  name="Power Feed 2B",
2329
+ status=pf_status,
2259
2330
  type=REDUNDANT,
2260
2331
  )
2261
2332
  PowerFeed.objects.create(
2262
2333
  power_panel=power_panels[0],
2263
2334
  rack=racks[2],
2264
2335
  name="Power Feed 3A",
2336
+ status=pf_status,
2265
2337
  type=PRIMARY,
2266
2338
  )
2267
2339
  PowerFeed.objects.create(
2268
2340
  power_panel=power_panels[1],
2269
2341
  rack=racks[2],
2270
2342
  name="Power Feed 3B",
2343
+ status=pf_status,
2271
2344
  type=REDUNDANT,
2272
2345
  )
2273
2346
 
@@ -2296,7 +2369,6 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
2296
2369
 
2297
2370
  class DeviceRedundancyGroupTest(APIViewTestCases.APIViewTestCase):
2298
2371
  model = DeviceRedundancyGroup
2299
- brief_fields = ["display", "failover_strategy", "id", "name", "slug", "url"]
2300
2372
  choices_fields = ["failover_strategy"]
2301
2373
 
2302
2374
  @classmethod