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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (780) hide show
  1. nautobot/apps/api.py +6 -8
  2. nautobot/apps/forms.py +0 -2
  3. nautobot/apps/ui.py +0 -8
  4. nautobot/circuits/api/serializers.py +9 -117
  5. nautobot/circuits/api/urls.py +1 -1
  6. nautobot/circuits/api/views.py +0 -1
  7. nautobot/circuits/forms.py +0 -65
  8. nautobot/circuits/migrations/0014_related_name_changes.py +1 -1
  9. nautobot/circuits/migrations/0016_tagsfield.py +34 -0
  10. nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
  11. nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
  12. nautobot/circuits/models.py +3 -87
  13. nautobot/circuits/navigation.py +14 -69
  14. nautobot/circuits/signals.py +0 -2
  15. nautobot/circuits/tables.py +39 -1
  16. nautobot/circuits/tests/integration/test_relationships.py +9 -9
  17. nautobot/circuits/tests/test_api.py +4 -8
  18. nautobot/circuits/tests/test_filters.py +10 -4
  19. nautobot/circuits/tests/test_models.py +5 -1
  20. nautobot/circuits/tests/test_views.py +27 -5
  21. nautobot/circuits/views.py +18 -10
  22. nautobot/core/api/__init__.py +8 -2
  23. nautobot/core/api/fields.py +15 -6
  24. nautobot/core/api/filter_backends.py +3 -2
  25. nautobot/core/api/metadata.py +237 -30
  26. nautobot/core/api/mixins.py +94 -0
  27. nautobot/core/api/pagination.py +4 -0
  28. nautobot/core/api/parsers.py +154 -0
  29. nautobot/core/api/renderers.py +153 -2
  30. nautobot/core/api/schema.py +46 -2
  31. nautobot/core/api/serializers.py +377 -35
  32. nautobot/core/api/urls.py +11 -3
  33. nautobot/core/api/utils.py +174 -2
  34. nautobot/core/api/versioning.py +32 -10
  35. nautobot/core/api/views.py +266 -72
  36. nautobot/core/apps/__init__.py +138 -220
  37. nautobot/core/celery/__init__.py +112 -41
  38. nautobot/core/celery/backends.py +19 -12
  39. nautobot/core/celery/control.py +46 -0
  40. nautobot/core/celery/encoders.py +53 -0
  41. nautobot/core/celery/log.py +38 -0
  42. nautobot/core/celery/schedulers.py +23 -4
  43. nautobot/core/celery/task.py +1 -16
  44. nautobot/core/checks.py +0 -27
  45. nautobot/core/choices.py +0 -113
  46. nautobot/core/{cli.py → cli/__init__.py} +1 -1
  47. nautobot/core/cli/__main__.py +3 -0
  48. nautobot/core/constants.py +0 -24
  49. nautobot/core/context_processors.py +12 -0
  50. nautobot/core/filters.py +2 -2
  51. nautobot/core/forms/__init__.py +0 -4
  52. nautobot/core/forms/fields.py +38 -65
  53. nautobot/core/forms/forms.py +4 -1
  54. nautobot/core/forms/utils.py +0 -52
  55. nautobot/core/graphql/schema.py +4 -27
  56. nautobot/core/jobs/__init__.py +75 -0
  57. nautobot/core/management/commands/build_ui.py +255 -0
  58. nautobot/core/management/commands/generate_test_data.py +3 -2
  59. nautobot/core/management/commands/post_upgrade.py +24 -24
  60. nautobot/core/models/__init__.py +26 -1
  61. nautobot/core/models/fields.py +24 -5
  62. nautobot/core/models/generics.py +2 -42
  63. nautobot/core/models/managers.py +5 -0
  64. nautobot/core/models/name_color_content_types.py +0 -14
  65. nautobot/core/models/tree_queries.py +14 -4
  66. nautobot/core/models/utils.py +5 -6
  67. nautobot/core/models/validators.py +17 -8
  68. nautobot/core/releases.py +8 -10
  69. nautobot/core/settings.py +80 -42
  70. nautobot/core/tables.py +5 -5
  71. nautobot/core/tasks.py +4 -7
  72. nautobot/core/templates/base.html +1 -49
  73. nautobot/core/templates/base_django.html +49 -0
  74. nautobot/core/templates/base_react.html +55 -0
  75. nautobot/core/templates/buttons/export.html +6 -4
  76. nautobot/core/templates/generic/object_bulk_create.html +10 -21
  77. nautobot/core/templates/generic/object_list.html +3 -1
  78. nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
  79. nautobot/core/templates/inc/footer.html +1 -0
  80. nautobot/core/templates/inc/javascript.html +0 -14
  81. nautobot/core/templates/inc/nav_menu.html +28 -33
  82. nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
  83. nautobot/core/templates/inc/relationships_table_rows.html +2 -2
  84. nautobot/core/templates/nautobot_config.py.j2 +8 -20
  85. nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
  86. nautobot/core/templates/rest_framework/api.html +8 -0
  87. nautobot/core/templatetags/buttons.py +32 -28
  88. nautobot/core/testing/__init__.py +47 -44
  89. nautobot/core/testing/api.py +362 -47
  90. nautobot/core/testing/filters.py +1 -1
  91. nautobot/core/testing/migrations.py +2 -0
  92. nautobot/core/testing/mixins.py +22 -9
  93. nautobot/core/testing/schema.py +2 -1
  94. nautobot/core/testing/views.py +21 -46
  95. nautobot/core/tests/integration/test_filters.py +17 -8
  96. nautobot/core/tests/integration/test_navbar.py +11 -34
  97. nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
  98. nautobot/core/tests/nautobot_config.py +2 -3
  99. nautobot/core/tests/test_api.py +290 -21
  100. nautobot/core/tests/test_checks.py +0 -7
  101. nautobot/core/tests/test_filters.py +107 -59
  102. nautobot/core/tests/test_forms.py +26 -92
  103. nautobot/core/tests/test_graphql.py +110 -77
  104. nautobot/core/tests/test_logging.py +4 -0
  105. nautobot/core/tests/test_managers.py +3 -1
  106. nautobot/core/tests/test_models.py +2 -0
  107. nautobot/core/tests/test_paginator.py +3 -1
  108. nautobot/core/tests/test_releases.py +12 -12
  109. nautobot/core/tests/test_templatetags_helpers.py +4 -4
  110. nautobot/core/tests/test_utils.py +32 -68
  111. nautobot/core/tests/test_views.py +12 -15
  112. nautobot/core/utils/data.py +17 -0
  113. nautobot/core/utils/deprecation.py +9 -6
  114. nautobot/core/utils/filtering.py +8 -3
  115. nautobot/core/utils/git.py +12 -4
  116. nautobot/core/utils/lookup.py +3 -1
  117. nautobot/core/utils/requests.py +1 -104
  118. nautobot/core/views/__init__.py +1 -0
  119. nautobot/core/views/generic.py +75 -110
  120. nautobot/core/views/mixins.py +52 -61
  121. nautobot/core/views/renderers.py +6 -7
  122. nautobot/core/views/utils.py +80 -0
  123. nautobot/dcim/api/serializers.py +160 -667
  124. nautobot/dcim/api/urls.py +1 -1
  125. nautobot/dcim/api/views.py +7 -44
  126. nautobot/dcim/choices.py +2 -0
  127. nautobot/dcim/filters/__init__.py +21 -0
  128. nautobot/dcim/form_mixins.py +1 -27
  129. nautobot/dcim/forms.py +19 -765
  130. nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -1
  131. nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -13
  132. nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -1
  133. nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -1
  134. nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
  135. nautobot/dcim/migrations/0035_related_name_changes.py +1 -1
  136. nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -1
  137. nautobot/dcim/migrations/0040_tagsfield.py +109 -0
  138. nautobot/dcim/migrations/{0040_ipam__namespaces.py → 0041_ipam__namespaces.py} +1 -1
  139. nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
  140. nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
  141. nautobot/dcim/models/cables.py +3 -33
  142. nautobot/dcim/models/device_component_templates.py +6 -0
  143. nautobot/dcim/models/device_components.py +12 -198
  144. nautobot/dcim/models/devices.py +30 -143
  145. nautobot/dcim/models/locations.py +3 -64
  146. nautobot/dcim/models/power.py +3 -50
  147. nautobot/dcim/models/racks.py +7 -84
  148. nautobot/dcim/navigation.py +141 -467
  149. nautobot/dcim/signals.py +0 -2
  150. nautobot/dcim/tables/locations.py +2 -2
  151. nautobot/dcim/tables/power.py +1 -2
  152. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
  153. nautobot/dcim/templates/dcim/devicetype.html +2 -2
  154. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
  155. nautobot/dcim/templates/dcim/location.html +16 -1
  156. nautobot/dcim/templates/dcim/locationtype.html +15 -0
  157. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
  158. nautobot/dcim/templates/dcim/rackgroup.html +0 -12
  159. nautobot/dcim/tests/test_api.py +166 -81
  160. nautobot/dcim/tests/test_cablepaths.py +41 -35
  161. nautobot/dcim/tests/test_filters.py +67 -23
  162. nautobot/dcim/tests/test_forms.py +5 -205
  163. nautobot/dcim/tests/test_graphql.py +7 -2
  164. nautobot/dcim/tests/test_migrations.py +6 -11
  165. nautobot/dcim/tests/test_models.py +182 -110
  166. nautobot/dcim/tests/test_natural_ordering.py +11 -8
  167. nautobot/dcim/tests/test_signals.py +6 -3
  168. nautobot/dcim/tests/test_views.py +197 -175
  169. nautobot/dcim/urls.py +11 -16
  170. nautobot/dcim/views.py +7 -134
  171. nautobot/docs/additional-features/caching.md +6 -87
  172. nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
  173. nautobot/docs/additional-features/jobs.md +177 -195
  174. nautobot/docs/administration/nautobot-server.md +6 -21
  175. nautobot/docs/administration/replicating-nautobot.md +0 -10
  176. nautobot/docs/configuration/optional-settings.md +32 -41
  177. nautobot/docs/configuration/required-settings.md +11 -52
  178. nautobot/docs/development/application-registry.md +2 -13
  179. nautobot/docs/development/extending-models.md +15 -17
  180. nautobot/docs/development/generic-views.md +0 -2
  181. nautobot/docs/development/getting-started.md +55 -5
  182. nautobot/docs/development/navigation-menu.md +22 -93
  183. nautobot/docs/development/react-ui.md +105 -0
  184. nautobot/docs/development/role-internals.md +1 -3
  185. nautobot/docs/development/style-guide.md +6 -4
  186. nautobot/docs/index.md +3 -2
  187. nautobot/docs/installation/migrating-from-netbox.md +11 -42
  188. nautobot/docs/installation/nautobot.md +1 -1
  189. nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  190. nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  191. nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  192. nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  193. nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
  194. nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  195. nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  196. nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  197. nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  198. nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  199. nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  200. nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  201. nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  202. nautobot/docs/installation/upgrading-from-nautobot-v1.md +170 -747
  203. nautobot/docs/models/dcim/device.md +3 -0
  204. nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
  205. nautobot/docs/models/extras/computedfield.md +4 -4
  206. nautobot/docs/models/extras/gitrepository.md +3 -0
  207. nautobot/docs/models/extras/job.md +1 -0
  208. nautobot/docs/models/extras/jobbutton.md +18 -13
  209. nautobot/docs/models/extras/jobhook.md +7 -4
  210. nautobot/docs/models/extras/jobresult.md +6 -2
  211. nautobot/docs/models/extras/relationship.md +2 -2
  212. nautobot/docs/models/extras/status.md +6 -19
  213. nautobot/docs/models/ipam/ipaddress.md +3 -0
  214. nautobot/docs/models/virtualization/virtualmachine.md +3 -0
  215. nautobot/docs/plugins/development.md +83 -21
  216. nautobot/docs/release-notes/version-1.5.md +53 -0
  217. nautobot/docs/release-notes/version-2.0.md +180 -0
  218. nautobot/docs/requirements.txt +1 -0
  219. nautobot/docs/rest-api/overview.md +384 -215
  220. nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
  221. nautobot/extras/admin.py +3 -5
  222. nautobot/extras/api/customfields.py +15 -39
  223. nautobot/extras/api/fields.py +0 -11
  224. nautobot/extras/api/mixins.py +45 -0
  225. nautobot/extras/api/relationships.py +63 -158
  226. nautobot/extras/api/serializers.py +165 -700
  227. nautobot/extras/api/urls.py +1 -1
  228. nautobot/extras/api/views.py +294 -280
  229. nautobot/extras/apps.py +4 -7
  230. nautobot/extras/choices.py +11 -9
  231. nautobot/extras/constants.py +9 -3
  232. nautobot/extras/datasources/__init__.py +2 -0
  233. nautobot/extras/datasources/git.py +135 -186
  234. nautobot/extras/datasources/registry.py +25 -35
  235. nautobot/extras/filters/__init__.py +20 -19
  236. nautobot/extras/filters/mixins.py +4 -4
  237. nautobot/extras/forms/forms.py +63 -127
  238. nautobot/extras/forms/mixins.py +23 -51
  239. nautobot/extras/health_checks.py +0 -33
  240. nautobot/extras/jobs.py +387 -565
  241. nautobot/extras/management/commands/runjob.py +24 -62
  242. nautobot/extras/managers.py +30 -7
  243. nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
  244. nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -1
  245. nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -1
  246. nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -7
  247. nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -32
  248. nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -1
  249. nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -1
  250. nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
  251. nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -1
  252. nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -2
  253. nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -1
  254. nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -1
  255. nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
  256. nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -1
  257. nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
  258. nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -9
  259. nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -1
  260. nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
  261. nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
  262. nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
  263. nautobot/extras/migrations/{0077_remove_slug.py → 0078_remove_slug.py} +1 -5
  264. nautobot/extras/migrations/0079_tagsfield.py +28 -0
  265. nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
  266. nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
  267. nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
  268. nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
  269. nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
  270. nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
  271. nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
  272. nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
  273. nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
  274. nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
  275. nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
  276. nautobot/extras/models/__init__.py +2 -3
  277. nautobot/extras/models/change_logging.py +0 -36
  278. nautobot/extras/models/customfields.py +39 -33
  279. nautobot/extras/models/datasources.py +48 -50
  280. nautobot/extras/models/groups.py +5 -6
  281. nautobot/extras/models/jobs.py +189 -321
  282. nautobot/extras/models/mixins.py +0 -71
  283. nautobot/extras/models/models.py +0 -19
  284. nautobot/extras/models/relationships.py +19 -13
  285. nautobot/extras/models/roles.py +0 -34
  286. nautobot/extras/models/secrets.py +2 -26
  287. nautobot/extras/models/statuses.py +6 -5
  288. nautobot/extras/models/tags.py +2 -17
  289. nautobot/extras/navigation.py +89 -307
  290. nautobot/extras/plugins/__init__.py +3 -120
  291. nautobot/extras/plugins/utils.py +0 -3
  292. nautobot/extras/plugins/validators.py +5 -4
  293. nautobot/extras/plugins/views.py +16 -3
  294. nautobot/extras/querysets.py +1 -7
  295. nautobot/extras/registry.py +3 -0
  296. nautobot/extras/signals.py +26 -60
  297. nautobot/extras/tables.py +34 -40
  298. nautobot/extras/tasks.py +0 -12
  299. nautobot/extras/templates/extras/configcontext.html +1 -1
  300. nautobot/extras/templates/extras/configcontextschema.html +16 -1
  301. nautobot/extras/templates/extras/customfield.html +0 -13
  302. nautobot/extras/templates/extras/gitrepository.html +3 -3
  303. nautobot/extras/templates/extras/inc/jobresult.html +10 -0
  304. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  305. nautobot/extras/templates/extras/job.html +35 -25
  306. nautobot/extras/templates/extras/job_approval_request.html +15 -30
  307. nautobot/extras/templates/extras/job_detail.html +13 -31
  308. nautobot/extras/templates/extras/job_edit.html +15 -17
  309. nautobot/extras/templates/extras/jobresult.html +24 -6
  310. nautobot/extras/templates/extras/scheduledjob.html +2 -2
  311. nautobot/extras/templates/extras/secret.html +28 -0
  312. nautobot/extras/templatetags/job_buttons.py +1 -0
  313. nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
  314. nautobot/extras/test_jobs/atomic_transaction.py +53 -0
  315. nautobot/extras/test_jobs/dry_run.py +29 -0
  316. nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
  317. nautobot/extras/test_jobs/duplicate_name2.py +9 -0
  318. nautobot/extras/test_jobs/fail.py +23 -0
  319. nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
  320. nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
  321. nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
  322. nautobot/extras/test_jobs/file_upload_pass.py +25 -0
  323. nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
  324. nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
  325. nautobot/extras/test_jobs/job_button_receiver.py +28 -0
  326. nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
  327. nautobot/extras/test_jobs/job_variables.py +88 -0
  328. nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
  329. nautobot/extras/test_jobs/log_redaction.py +20 -0
  330. nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
  331. nautobot/extras/test_jobs/modify_db.py +25 -0
  332. nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
  333. nautobot/extras/test_jobs/object_var_optional.py +21 -0
  334. nautobot/extras/test_jobs/object_var_required.py +21 -0
  335. nautobot/extras/test_jobs/object_vars.py +26 -0
  336. nautobot/extras/test_jobs/pass.py +25 -0
  337. nautobot/extras/test_jobs/profiling.py +32 -0
  338. nautobot/extras/test_jobs/read_only_job.py +15 -0
  339. nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
  340. nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
  341. nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
  342. nautobot/extras/tests/integration/test_computedfields.py +1 -1
  343. nautobot/extras/tests/integration/test_configcontextschema.py +5 -3
  344. nautobot/extras/tests/integration/test_customfields.py +4 -2
  345. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  346. nautobot/extras/tests/integration/test_jobs.py +25 -27
  347. nautobot/extras/tests/integration/test_notes.py +8 -4
  348. nautobot/extras/tests/integration/test_relationships.py +2 -2
  349. nautobot/extras/tests/test_api.py +649 -642
  350. nautobot/extras/tests/test_changelog.py +3 -3
  351. nautobot/extras/tests/test_context_managers.py +5 -3
  352. nautobot/extras/tests/test_customfields.py +92 -50
  353. nautobot/extras/tests/test_datasources.py +189 -112
  354. nautobot/extras/tests/test_dynamicgroups.py +7 -8
  355. nautobot/extras/tests/test_filters.py +137 -89
  356. nautobot/extras/tests/test_forms.py +73 -75
  357. nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
  358. nautobot/extras/tests/test_jobs.py +262 -263
  359. nautobot/extras/tests/test_migrations.py +4 -3
  360. nautobot/extras/tests/test_models.py +116 -161
  361. nautobot/extras/tests/test_plugins.py +38 -60
  362. nautobot/extras/tests/test_relationships.py +167 -120
  363. nautobot/extras/tests/test_tags.py +6 -11
  364. nautobot/extras/tests/test_utils.py +31 -1
  365. nautobot/extras/tests/test_views.py +201 -145
  366. nautobot/extras/tests/test_webhooks.py +6 -2
  367. nautobot/extras/urls.py +42 -42
  368. nautobot/extras/utils.py +137 -163
  369. nautobot/extras/views.py +78 -152
  370. nautobot/ipam/api/fields.py +17 -0
  371. nautobot/ipam/api/serializers.py +58 -164
  372. nautobot/ipam/api/urls.py +1 -1
  373. nautobot/ipam/api/views.py +3 -2
  374. nautobot/ipam/apps.py +1 -2
  375. nautobot/ipam/filters.py +1 -10
  376. nautobot/ipam/forms.py +4 -177
  377. nautobot/ipam/lookups.py +1 -0
  378. nautobot/ipam/management/commands/__init__.py +0 -0
  379. nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
  380. nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -1
  381. nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -38
  382. nautobot/ipam/migrations/0020_related_name_changes.py +1 -1
  383. nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +2 -2
  384. nautobot/ipam/migrations/0028_tagsfield.py +44 -0
  385. nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
  386. nautobot/ipam/migrations/{0028_ipam__namespaces.py → 0030_ipam__namespaces.py} +77 -28
  387. nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
  388. nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
  389. nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
  390. nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
  391. nautobot/ipam/models.py +100 -236
  392. nautobot/ipam/navigation.py +36 -181
  393. nautobot/ipam/querysets.py +20 -25
  394. nautobot/ipam/signals.py +49 -6
  395. nautobot/ipam/tables.py +10 -3
  396. nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
  397. nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
  398. nautobot/ipam/templates/ipam/namespace_retrieve.html +17 -4
  399. nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
  400. nautobot/ipam/templates/ipam/prefix.html +1 -1
  401. nautobot/ipam/templates/ipam/vlangroup.html +0 -13
  402. nautobot/ipam/templates/ipam/vrf_edit.html +6 -0
  403. nautobot/ipam/tests/integration/test_prefixes.py +3 -26
  404. nautobot/ipam/tests/test_api.py +22 -19
  405. nautobot/ipam/tests/test_filters.py +59 -23
  406. nautobot/ipam/tests/test_migrations.py +6 -10
  407. nautobot/ipam/tests/test_models.py +323 -198
  408. nautobot/ipam/tests/test_ordering.py +2 -2
  409. nautobot/ipam/tests/test_querysets.py +44 -24
  410. nautobot/ipam/tests/test_views.py +73 -26
  411. nautobot/ipam/urls.py +16 -0
  412. nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
  413. nautobot/ipam/utils/migrations.py +713 -0
  414. nautobot/ipam/views.py +137 -20
  415. nautobot/project-static/docs/404.html +1178 -10
  416. nautobot/project-static/docs/additional-features/caching.html +1224 -159
  417. nautobot/project-static/docs/additional-features/change-logging.html +1180 -12
  418. nautobot/project-static/docs/additional-features/config-contexts.html +1180 -12
  419. nautobot/project-static/docs/additional-features/graphql.html +1179 -11
  420. nautobot/project-static/docs/additional-features/healthcheck.html +1180 -12
  421. nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1184 -12
  422. nautobot/project-static/docs/additional-features/jobs.html +1514 -328
  423. nautobot/project-static/docs/additional-features/napalm.html +1180 -12
  424. nautobot/project-static/docs/additional-features/prometheus-metrics.html +1180 -12
  425. nautobot/project-static/docs/additional-features/template-filters.html +1180 -12
  426. nautobot/project-static/docs/administration/celery-queues.html +1178 -10
  427. nautobot/project-static/docs/administration/nautobot-server.html +1451 -304
  428. nautobot/project-static/docs/administration/nautobot-shell.html +1178 -10
  429. nautobot/project-static/docs/administration/permissions.html +1178 -10
  430. nautobot/project-static/docs/administration/replicating-nautobot.html +1262 -113
  431. nautobot/project-static/docs/apps/index.html +1178 -10
  432. nautobot/project-static/docs/apps/nautobot-apps.html +1178 -10
  433. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1580 -426
  434. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1178 -10
  435. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3481 -1838
  436. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1178 -10
  437. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1178 -10
  438. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1185 -11
  439. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1719 -551
  440. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2062 -930
  441. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +1946 -659
  442. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1180 -12
  443. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1189 -21
  444. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9283 -6218
  445. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2734 -2122
  446. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1178 -10
  447. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2337 -1300
  448. nautobot/project-static/docs/configuration/authentication/ldap.html +1178 -10
  449. nautobot/project-static/docs/configuration/authentication/remote.html +1178 -10
  450. nautobot/project-static/docs/configuration/authentication/sso.html +1178 -10
  451. nautobot/project-static/docs/configuration/index.html +1178 -10
  452. nautobot/project-static/docs/configuration/optional-settings.html +1311 -160
  453. nautobot/project-static/docs/configuration/required-settings.html +1312 -211
  454. nautobot/project-static/docs/core-functionality/circuits.html +1178 -10
  455. nautobot/project-static/docs/core-functionality/device-types.html +1178 -10
  456. nautobot/project-static/docs/core-functionality/devices.html +1182 -10
  457. nautobot/project-static/docs/core-functionality/ipam.html +1182 -10
  458. nautobot/project-static/docs/core-functionality/power.html +1178 -10
  459. nautobot/project-static/docs/core-functionality/secrets.html +1178 -10
  460. nautobot/project-static/docs/core-functionality/services.html +1178 -10
  461. nautobot/project-static/docs/core-functionality/sites-and-racks.html +1178 -10
  462. nautobot/project-static/docs/core-functionality/tenancy.html +1178 -10
  463. nautobot/project-static/docs/core-functionality/virtualization.html +1182 -10
  464. nautobot/project-static/docs/core-functionality/vlans.html +1179 -11
  465. nautobot/project-static/docs/development/application-registry.html +1190 -42
  466. nautobot/project-static/docs/development/best-practices.html +1178 -10
  467. nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1178 -10
  468. nautobot/project-static/docs/development/extending-models.html +1238 -83
  469. nautobot/project-static/docs/development/generic-views.html +1180 -14
  470. nautobot/project-static/docs/development/getting-started.html +1365 -90
  471. nautobot/project-static/docs/development/homepage.html +1178 -10
  472. nautobot/project-static/docs/development/index.html +1178 -10
  473. nautobot/project-static/docs/development/model-features.html +1178 -10
  474. nautobot/project-static/docs/development/natural-keys.html +1178 -10
  475. nautobot/project-static/docs/development/navigation-menu.html +1215 -125
  476. nautobot/project-static/docs/development/react-ui.html +4199 -0
  477. nautobot/project-static/docs/development/release-checklist.html +1178 -10
  478. nautobot/project-static/docs/development/role-internals.html +1179 -12
  479. nautobot/project-static/docs/development/style-guide.html +1188 -19
  480. nautobot/project-static/docs/development/templates.html +1178 -10
  481. nautobot/project-static/docs/development/testing.html +1178 -10
  482. nautobot/project-static/docs/development/user-preferences.html +1178 -10
  483. nautobot/project-static/docs/docker/index.html +1178 -10
  484. nautobot/project-static/docs/index.html +1183 -12
  485. nautobot/project-static/docs/installation/centos.html +1178 -10
  486. nautobot/project-static/docs/installation/external-authentication.html +1178 -10
  487. nautobot/project-static/docs/installation/http-server.html +1178 -10
  488. nautobot/project-static/docs/installation/index.html +1178 -10
  489. nautobot/project-static/docs/installation/migrating-from-netbox.html +1305 -189
  490. nautobot/project-static/docs/installation/migrating-from-postgresql.html +1178 -10
  491. nautobot/project-static/docs/installation/nautobot.html +1179 -11
  492. nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1178 -10
  493. nautobot/project-static/docs/installation/selinux-troubleshooting.html +1178 -10
  494. nautobot/project-static/docs/installation/services.html +1178 -10
  495. nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  496. nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  497. nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  498. nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  499. nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
  500. nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  501. nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  502. nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  503. nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  504. nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  505. nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  506. nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  507. nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  508. nautobot/project-static/docs/installation/ubuntu.html +1178 -10
  509. nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +3823 -2152
  510. nautobot/project-static/docs/installation/upgrading.html +1178 -10
  511. nautobot/project-static/docs/models/circuits/circuit.html +1293 -103
  512. nautobot/project-static/docs/models/circuits/circuittermination.html +1293 -103
  513. nautobot/project-static/docs/models/circuits/circuittype.html +1293 -103
  514. nautobot/project-static/docs/models/circuits/provider.html +1293 -103
  515. nautobot/project-static/docs/models/circuits/providernetwork.html +1293 -103
  516. nautobot/project-static/docs/models/dcim/cable.html +1324 -103
  517. nautobot/project-static/docs/models/dcim/consoleport.html +1293 -103
  518. nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1293 -103
  519. nautobot/project-static/docs/models/dcim/consoleserverport.html +1293 -103
  520. nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1293 -103
  521. nautobot/project-static/docs/models/dcim/device.html +1326 -132
  522. nautobot/project-static/docs/models/dcim/devicebay.html +1293 -103
  523. nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1293 -103
  524. nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1379 -97
  525. nautobot/project-static/docs/models/dcim/devicetype.html +1293 -103
  526. nautobot/project-static/docs/models/dcim/frontport.html +1293 -103
  527. nautobot/project-static/docs/models/dcim/frontporttemplate.html +1293 -103
  528. nautobot/project-static/docs/models/dcim/interface.html +1293 -103
  529. nautobot/project-static/docs/models/dcim/interfacetemplate.html +1293 -103
  530. nautobot/project-static/docs/models/dcim/inventoryitem.html +1293 -103
  531. nautobot/project-static/docs/models/dcim/location.html +1293 -103
  532. nautobot/project-static/docs/models/dcim/locationtype.html +1293 -103
  533. nautobot/project-static/docs/models/dcim/manufacturer.html +1292 -102
  534. nautobot/project-static/docs/models/dcim/platform.html +1272 -82
  535. nautobot/project-static/docs/models/dcim/powerfeed.html +1270 -80
  536. nautobot/project-static/docs/models/dcim/poweroutlet.html +1272 -82
  537. nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1272 -82
  538. nautobot/project-static/docs/models/dcim/powerpanel.html +1270 -80
  539. nautobot/project-static/docs/models/dcim/powerport.html +1272 -82
  540. nautobot/project-static/docs/models/dcim/powerporttemplate.html +1272 -82
  541. nautobot/project-static/docs/models/dcim/rack.html +1272 -82
  542. nautobot/project-static/docs/models/dcim/rackgroup.html +1272 -82
  543. nautobot/project-static/docs/models/dcim/rackreservation.html +1272 -82
  544. nautobot/project-static/docs/models/dcim/rearport.html +1286 -96
  545. nautobot/project-static/docs/models/dcim/rearporttemplate.html +1286 -96
  546. nautobot/project-static/docs/models/dcim/region.html +1178 -10
  547. nautobot/project-static/docs/models/dcim/site.html +1178 -10
  548. nautobot/project-static/docs/models/dcim/virtualchassis.html +1284 -94
  549. nautobot/project-static/docs/models/extras/computedfield.html +1184 -16
  550. nautobot/project-static/docs/models/extras/configcontext.html +1314 -86
  551. nautobot/project-static/docs/models/extras/configcontextschema.html +1276 -86
  552. nautobot/project-static/docs/models/extras/customfield.html +1180 -12
  553. nautobot/project-static/docs/models/extras/customlink.html +1180 -12
  554. nautobot/project-static/docs/models/extras/dynamicgroup.html +1180 -12
  555. nautobot/project-static/docs/models/extras/exporttemplate.html +1180 -12
  556. nautobot/project-static/docs/models/extras/gitrepository.html +1184 -12
  557. nautobot/project-static/docs/models/extras/graphqlquery.html +1321 -86
  558. nautobot/project-static/docs/models/extras/imageattachment.html +1276 -86
  559. nautobot/project-static/docs/models/extras/job.html +1277 -86
  560. nautobot/project-static/docs/models/extras/jobbutton.html +1201 -29
  561. nautobot/project-static/docs/models/extras/jobhook.html +1188 -16
  562. nautobot/project-static/docs/models/extras/joblogentry.html +1274 -84
  563. nautobot/project-static/docs/models/extras/jobresult.html +1364 -169
  564. nautobot/project-static/docs/models/extras/note.html +1180 -12
  565. nautobot/project-static/docs/models/extras/relationship.html +1182 -14
  566. nautobot/project-static/docs/models/extras/role.html +1320 -86
  567. nautobot/project-static/docs/models/extras/secret.html +1314 -86
  568. nautobot/project-static/docs/models/extras/secretsgroup.html +1276 -86
  569. nautobot/project-static/docs/models/extras/status.html +1188 -59
  570. nautobot/project-static/docs/models/extras/tag.html +1180 -12
  571. nautobot/project-static/docs/models/extras/webhook.html +1180 -12
  572. nautobot/project-static/docs/models/ipam/ipaddress.html +1327 -102
  573. nautobot/project-static/docs/models/ipam/prefix.html +1276 -86
  574. nautobot/project-static/docs/models/ipam/rir.html +1276 -86
  575. nautobot/project-static/docs/models/ipam/routetarget.html +1276 -86
  576. nautobot/project-static/docs/models/ipam/service.html +1276 -86
  577. nautobot/project-static/docs/models/ipam/vlan.html +1276 -86
  578. nautobot/project-static/docs/models/ipam/vlangroup.html +1276 -86
  579. nautobot/project-static/docs/models/ipam/vrf.html +1276 -86
  580. nautobot/project-static/docs/models/tenancy/tenant.html +1276 -86
  581. nautobot/project-static/docs/models/tenancy/tenantgroup.html +1276 -86
  582. nautobot/project-static/docs/models/users/objectpermission.html +1314 -86
  583. nautobot/project-static/docs/models/users/token.html +1276 -86
  584. nautobot/project-static/docs/models/virtualization/cluster.html +1276 -86
  585. nautobot/project-static/docs/models/virtualization/clustergroup.html +1276 -86
  586. nautobot/project-static/docs/models/virtualization/clustertype.html +1276 -86
  587. nautobot/project-static/docs/models/virtualization/virtualmachine.html +1321 -127
  588. nautobot/project-static/docs/models/virtualization/vminterface.html +1276 -86
  589. nautobot/project-static/docs/objects.inv +0 -0
  590. nautobot/project-static/docs/plugins/development.html +1726 -495
  591. nautobot/project-static/docs/plugins/index.html +1178 -10
  592. nautobot/project-static/docs/plugins/porting-from-netbox.html +1178 -10
  593. nautobot/project-static/docs/release-notes/index.html +1178 -10
  594. nautobot/project-static/docs/release-notes/version-1.0.html +1178 -10
  595. nautobot/project-static/docs/release-notes/version-1.1.html +1178 -10
  596. nautobot/project-static/docs/release-notes/version-1.2.html +1178 -10
  597. nautobot/project-static/docs/release-notes/version-1.3.html +1178 -10
  598. nautobot/project-static/docs/release-notes/version-1.4.html +1178 -10
  599. nautobot/project-static/docs/release-notes/version-1.5.html +1608 -225
  600. nautobot/project-static/docs/release-notes/version-2.0.html +1547 -47
  601. nautobot/project-static/docs/requirements.txt +1 -0
  602. nautobot/project-static/docs/rest-api/authentication.html +1179 -11
  603. nautobot/project-static/docs/rest-api/filtering.html +1178 -10
  604. nautobot/project-static/docs/rest-api/overview.html +1841 -446
  605. nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
  606. nautobot/project-static/docs/search/search_index.json +1 -1
  607. nautobot/project-static/docs/sitemap.xml +197 -187
  608. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  609. nautobot/project-static/docs/user-guides/custom-fields.html +1178 -10
  610. nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1178 -10
  611. nautobot/project-static/docs/user-guides/getting-started/index.html +1178 -10
  612. nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1178 -10
  613. nautobot/project-static/docs/user-guides/getting-started/ipam.html +1178 -10
  614. nautobot/project-static/docs/user-guides/getting-started/platforms.html +1178 -10
  615. nautobot/project-static/docs/user-guides/getting-started/regions.html +1178 -10
  616. nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1178 -10
  617. nautobot/project-static/docs/user-guides/getting-started/tenants.html +1178 -10
  618. nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1178 -10
  619. nautobot/project-static/docs/user-guides/git-data-source.html +1178 -10
  620. nautobot/project-static/docs/user-guides/graphql.html +1178 -10
  621. nautobot/project-static/docs/user-guides/relationships.html +1178 -10
  622. nautobot/project-static/docs/user-guides/s3-django-storage.html +1178 -10
  623. nautobot/project-static/js/forms.js +16 -9
  624. nautobot/project-static/js/theme.js +5 -0
  625. nautobot/tenancy/api/serializers.py +4 -32
  626. nautobot/tenancy/api/urls.py +1 -1
  627. nautobot/tenancy/forms.py +0 -28
  628. nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
  629. nautobot/tenancy/models.py +0 -25
  630. nautobot/tenancy/navigation.py +6 -39
  631. nautobot/tenancy/templates/tenancy/tenant.html +12 -12
  632. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  633. nautobot/tenancy/tests/test_api.py +1 -3
  634. nautobot/tenancy/tests/test_filters.py +10 -5
  635. nautobot/tenancy/views.py +0 -2
  636. nautobot/ui/.eslintignore +6 -0
  637. nautobot/ui/.gitignore +10 -0
  638. nautobot/ui/.prettierignore +9 -0
  639. nautobot/ui/.prettierrc +4 -0
  640. nautobot/ui/README.md +33 -0
  641. nautobot/ui/app_imports.js.j2 +7 -0
  642. nautobot/ui/craco.config.js +46 -0
  643. nautobot/ui/jsconfig-base.json +11 -0
  644. nautobot/ui/jsconfig.json +5 -0
  645. nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
  646. nautobot/ui/package-lock.json +21451 -0
  647. nautobot/ui/package.json +70 -0
  648. nautobot/ui/public/index.html +47 -0
  649. nautobot/ui/public/logo192.png +0 -0
  650. nautobot/ui/public/logo512.png +0 -0
  651. nautobot/ui/public/manifest.json +25 -0
  652. nautobot/ui/public/nautobot_logo.svg +131 -0
  653. nautobot/ui/public/robots.txt +3 -0
  654. nautobot/ui/src/App.js +71 -0
  655. nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
  656. nautobot/ui/src/components/AppTab.js +40 -0
  657. nautobot/ui/src/components/Apps.js +60 -0
  658. nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
  659. nautobot/ui/src/components/HomePanel.js +58 -0
  660. nautobot/ui/src/components/JobHistoryTable.js +78 -0
  661. nautobot/ui/src/components/Layout.js +53 -0
  662. nautobot/ui/src/components/LoadingWidget.js +25 -0
  663. nautobot/ui/src/components/Navbar.js +116 -0
  664. nautobot/ui/src/components/NotificationPopover.js +27 -0
  665. nautobot/ui/src/components/ObjectListTable.js +209 -0
  666. nautobot/ui/src/components/ReferenceDataTag.js +35 -0
  667. nautobot/ui/src/components/RouterButton.js +10 -0
  668. nautobot/ui/src/components/RouterLink.js +10 -0
  669. nautobot/ui/src/components/SidebarNav.js +147 -0
  670. nautobot/ui/src/components/Table.js +48 -0
  671. nautobot/ui/src/components/TableItem.js +71 -0
  672. nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
  673. nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
  674. nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
  675. nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
  676. nautobot/ui/src/components/__tests__/Table.test.js +36 -0
  677. nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
  678. nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
  679. nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
  680. nautobot/ui/src/components/pagination.js +93 -0
  681. nautobot/ui/src/components/paginator.js +79 -0
  682. nautobot/ui/src/components/paginator_form.js +43 -0
  683. nautobot/ui/src/components/usePagination.js +57 -0
  684. nautobot/ui/src/constants/apiPath.js +10 -0
  685. nautobot/ui/src/constants/icons.js +15 -0
  686. nautobot/ui/src/constants/size.js +15 -0
  687. nautobot/ui/src/index.js +65 -0
  688. nautobot/ui/src/reportWebVitals.js +15 -0
  689. nautobot/ui/src/router.js +77 -0
  690. nautobot/ui/src/utils/api.js +131 -0
  691. nautobot/ui/src/utils/app-import.js +15 -0
  692. nautobot/ui/src/utils/color.js +15 -0
  693. nautobot/ui/src/utils/date.js +14 -0
  694. nautobot/ui/src/utils/index.js +15 -0
  695. nautobot/ui/src/utils/navigation.js +32 -0
  696. nautobot/ui/src/utils/session.js +64 -0
  697. nautobot/ui/src/utils/store.js +242 -0
  698. nautobot/ui/src/utils/string.js +6 -0
  699. nautobot/ui/src/utils/url.js +4 -0
  700. nautobot/ui/src/views/Home.js +138 -0
  701. nautobot/ui/src/views/InstalledApps.js +80 -0
  702. nautobot/ui/src/views/Login.js +48 -0
  703. nautobot/ui/src/views/Logout.js +20 -0
  704. nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
  705. nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
  706. nautobot/ui/src/views/__tests__/Login.test.js +15 -0
  707. nautobot/ui/src/views/generic/GenericView.js +142 -0
  708. nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
  709. nautobot/ui/src/views/generic/ObjectList.js +127 -0
  710. nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
  711. nautobot/users/admin.py +1 -1
  712. nautobot/users/api/serializers.py +51 -61
  713. nautobot/users/api/urls.py +1 -1
  714. nautobot/users/api/views.py +53 -2
  715. nautobot/users/tests/test_api.py +110 -25
  716. nautobot/virtualization/api/serializers.py +18 -130
  717. nautobot/virtualization/api/urls.py +1 -1
  718. nautobot/virtualization/api/views.py +1 -22
  719. nautobot/virtualization/forms.py +13 -99
  720. nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -1
  721. nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -11
  722. nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -1
  723. nautobot/virtualization/migrations/0018_related_name_changes.py +1 -1
  724. nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
  725. nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
  726. nautobot/virtualization/migrations/{0021_ipam__namespaces.py → 0023_ipam__namespaces.py} +2 -2
  727. nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
  728. nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
  729. nautobot/virtualization/models.py +31 -123
  730. nautobot/virtualization/navigation.py +18 -99
  731. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -1
  732. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
  733. nautobot/virtualization/tests/test_api.py +25 -26
  734. nautobot/virtualization/tests/test_filters.py +41 -15
  735. nautobot/virtualization/tests/test_models.py +31 -7
  736. nautobot/virtualization/tests/test_views.py +42 -25
  737. nautobot/virtualization/views.py +7 -6
  738. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +3 -7
  739. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +744 -602
  740. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
  741. nautobot/circuits/api/nested_serializers.py +0 -69
  742. nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
  743. nautobot/dcim/api/nested_serializers.py +0 -356
  744. nautobot/dcim/templates/dcim/device_import.html +0 -5
  745. nautobot/dcim/templates/dcim/device_import_child.html +0 -5
  746. nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
  747. nautobot/extras/api/nested_serializers.py +0 -353
  748. nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -41
  749. nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -46
  750. nautobot/extras/reports.py +0 -60
  751. nautobot/extras/scripts.py +0 -72
  752. nautobot/extras/tests/example_jobs/script_variables.py +0 -67
  753. nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
  754. nautobot/extras/tests/example_jobs/test_fail.py +0 -16
  755. nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
  756. nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
  757. nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
  758. nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
  759. nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
  760. nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
  761. nautobot/extras/tests/example_jobs/test_modify_db.py +0 -18
  762. nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
  763. nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
  764. nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
  765. nautobot/extras/tests/example_jobs/test_pass.py +0 -19
  766. nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
  767. nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
  768. nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
  769. nautobot/ipam/api/nested_serializers.py +0 -159
  770. nautobot/ipam/migrations/0029_ipam__prefix__add_parent.py +0 -31
  771. nautobot/ipam/migrations/0030_ipam__prefix__data_migration.py +0 -13
  772. nautobot/ipam/migrations/0031_ipam__ipaddress__add_parent.py +0 -41
  773. nautobot/ipam/migrations/0032_ipam__ipaddress__data_migration.py +0 -11
  774. nautobot/tenancy/api/nested_serializers.py +0 -31
  775. nautobot/users/api/nested_serializers.py +0 -67
  776. nautobot/virtualization/api/nested_serializers.py +0 -65
  777. /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
  778. /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
  779. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
  780. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
@@ -94,15 +94,19 @@ def create_test_device(name):
94
94
  Convenience method for creating a Device (e.g. for component testing).
95
95
  """
96
96
  location_type, _ = LocationType.objects.get_or_create(name="Campus")
97
+ location_status = Status.objects.get_for_model(Location).first()
97
98
  location, _ = Location.objects.get_or_create(
98
- name="Test Location 1", slug="test-location-1", location_type=location_type
99
+ name="Test Location 1", slug="test-location-1", location_type=location_type, status=location_status
99
100
  )
100
101
  manufacturer, _ = Manufacturer.objects.get_or_create(name="Manufacturer 1")
101
102
  devicetype, _ = DeviceType.objects.get_or_create(model="Device Type 1", manufacturer=manufacturer)
102
103
  devicerole, _ = Role.objects.get_or_create(name="Device Role")
103
104
  device_ct = ContentType.objects.get_for_model(Device)
104
105
  devicerole.content_types.add(device_ct)
105
- device = Device.objects.create(name=name, location=location, device_type=devicetype, role=devicerole)
106
+ devicestatus = Status.objects.get_for_model(Device).first()
107
+ device = Device.objects.create(
108
+ name=name, location=location, device_type=devicetype, role=devicerole, status=devicestatus
109
+ )
106
110
 
107
111
  return device
108
112
 
@@ -198,8 +202,8 @@ class LocationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
198
202
  cls.csv_data = (
199
203
  "name,slug,location_type,parent,status,tenant,description",
200
204
  f'Root 3,root-3,"{lt1.name}",,{status.name},,',
201
- f'Intermediate 2,intermediate-2,"{lt2.name}","{loc2.name}",{status.name},"{tenant.name}",Hello world!',
202
- f'Leaf 2,leaf-2,"{lt3.name}","{loc3.name}",{status.name},"{tenant.name}",',
205
+ f'Intermediate 2,intermediate-2,"{lt2.name}",{loc2.natural_key_slug},{status.name},"{tenant.name}",Hello world!',
206
+ f'Leaf 2,leaf-2,"{lt3.name}",{loc3.natural_key_slug},{status.name},"{tenant.name}",',
203
207
  )
204
208
 
205
209
  cls.bulk_edit_data = {
@@ -237,10 +241,10 @@ class RackGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
237
241
 
238
242
  cls.csv_data = (
239
243
  "location,name,slug,description",
240
- f"{location.name},Rack Group 4,rack-group-4,Fourth rack group",
241
- f"{location.name},Rack Group 5,rack-group-5,Fifth rack group",
242
- f"{location.name},Rack Group 6,rack-group-6,Sixth rack group",
243
- f"{location.name},Rack Group 7,,Seventh rack group",
244
+ f"{location.natural_key_slug},Rack Group 4,rack-group-4,Fourth rack group",
245
+ f"{location.natural_key_slug},Rack Group 5,rack-group-5,Fifth rack group",
246
+ f"{location.natural_key_slug},Rack Group 6,rack-group-6,Sixth rack group",
247
+ f"{location.natural_key_slug},Rack Group 7,,Seventh rack group",
244
248
  )
245
249
  cls.slug_test_object = "Rack Group 8"
246
250
  cls.slug_source = "name"
@@ -258,7 +262,8 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
258
262
 
259
263
  rack_group = RackGroup.objects.create(name="Rack Group 1", slug="rack-group-1", location=location)
260
264
 
261
- rack = Rack.objects.create(name="Rack 1", location=location, rack_group=rack_group)
265
+ rack_status = Status.objects.get_for_model(Rack).first()
266
+ rack = Rack.objects.create(name="Rack 1", location=location, rack_group=rack_group, status=rack_status)
262
267
 
263
268
  RackReservation.objects.create(rack=rack, user=user2, units=[1, 2, 3], description="Reservation 1")
264
269
  RackReservation.objects.create(rack=rack, user=user2, units=[4, 5, 6], description="Reservation 2")
@@ -274,10 +279,10 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
274
279
  }
275
280
 
276
281
  cls.csv_data = (
277
- "location,rack_group,rack,units,description",
278
- f'{location.name},Rack Group 1,Rack 1,"10,11,12",Reservation 1',
279
- f'{location.name},Rack Group 1,Rack 1,"13,14,15",Reservation 2',
280
- f'{location.name},Rack Group 1,Rack 1,"16,17,18",Reservation 3',
282
+ "rack,units,description",
283
+ f'{rack.natural_key_slug},"10,11,12",Reservation 1',
284
+ f"{rack.natural_key_slug},13,Reservation 2",
285
+ f'{rack.natural_key_slug},"16,17,18",Reservation 3',
281
286
  )
282
287
 
283
288
  cls.bulk_edit_data = {
@@ -348,24 +353,24 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
348
353
  # Create a class racks variable
349
354
  cls.racks = racks
350
355
 
351
- # cls.relationships = (
352
- # Relationship(
353
- # name="Backup Locations",
354
- # slug="backup-locations",
355
- # type=RelationshipTypeChoices.TYPE_MANY_TO_MANY,
356
- # source_type=ContentType.objects.get_for_model(Rack),
357
- # source_label="Backup location(s)",
358
- # destination_type=ContentType.objects.get_for_model(Location),
359
- # destination_label="Racks using this location as a backup",
360
- # ),
361
- # )
362
- # for relationship in cls.relationships:
363
- # relationship.validated_save()
364
-
365
- # for rack in racks:
366
- # RelationshipAssociation(
367
- # relationship=cls.relationships[0], source=rack, destination=cls.locations[1]
368
- # ).validated_save()
356
+ cls.relationships = (
357
+ Relationship(
358
+ label="Backup Locations",
359
+ key="backup_locations",
360
+ type=RelationshipTypeChoices.TYPE_MANY_TO_MANY,
361
+ source_type=ContentType.objects.get_for_model(Rack),
362
+ source_label="Backup location(s)",
363
+ destination_type=ContentType.objects.get_for_model(Location),
364
+ destination_label="Racks using this location as a backup",
365
+ ),
366
+ )
367
+ for relationship in cls.relationships:
368
+ relationship.validated_save()
369
+
370
+ for rack in racks:
371
+ RelationshipAssociation(
372
+ relationship=cls.relationships[0], source=rack, destination=cls.locations[1]
373
+ ).validated_save()
369
374
 
370
375
  cls.form_data = {
371
376
  "name": "Rack X",
@@ -392,9 +397,9 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
392
397
 
393
398
  cls.csv_data = (
394
399
  "location,rack_group,name,width,u_height,status",
395
- f"{cls.locations[0].name},,Rack 4,19,42,{statuses[0].name}",
396
- f"{cls.locations[0].name},Rack Group 1,Rack 5,19,42,{statuses[1].name}",
397
- f"{cls.locations[1].name},Rack Group 2,Rack 6,19,42,{statuses[2].name}",
400
+ f"{cls.locations[0].natural_key_slug},,Rack 4,19,42,{statuses[0].name}",
401
+ f"{cls.locations[0].natural_key_slug},{rackgroups[0].natural_key_slug},Rack 5,19,42,{statuses[1].name}",
402
+ f"{cls.locations[1].natural_key_slug},{rackgroups[1].natural_key_slug},Rack 6,19,42,{statuses[2].name}",
398
403
  )
399
404
 
400
405
  cls.bulk_edit_data = {
@@ -458,6 +463,7 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
458
463
 
459
464
  # Create Power Port for device
460
465
  powerport1 = PowerPort.objects.create(device=devices[0], name="Power Port 11")
466
+ pf_status = Status.objects.get_for_model(PowerFeed).first()
461
467
  powerfeed1 = PowerFeed.objects.create(
462
468
  power_panel=self.powerpanels[0],
463
469
  name="Power Feed 11",
@@ -465,6 +471,7 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
465
471
  voltage=240,
466
472
  amperage=20,
467
473
  rack=self.racks[0],
474
+ status=pf_status,
468
475
  )
469
476
  powerfeed2 = PowerFeed.objects.create(
470
477
  power_panel=self.powerpanels[0],
@@ -473,6 +480,7 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
473
480
  voltage=240,
474
481
  amperage=20,
475
482
  rack=self.racks[0],
483
+ status=pf_status,
476
484
  )
477
485
 
478
486
  # Create power outlet to the power port
@@ -1156,9 +1164,10 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1156
1164
 
1157
1165
  rack_group = RackGroup.objects.create(location=locations[0], name="Rack Group 1", slug="rack-group-1")
1158
1166
 
1167
+ rack_status = Status.objects.get_for_model(Rack).first()
1159
1168
  racks = (
1160
- Rack.objects.create(name="Rack 1", location=locations[0], rack_group=rack_group),
1161
- Rack.objects.create(name="Rack 2", location=locations[1]),
1169
+ Rack.objects.create(name="Rack 1", location=locations[0], rack_group=rack_group, status=rack_status),
1170
+ Rack.objects.create(name="Rack 2", location=locations[1], status=rack_status),
1162
1171
  )
1163
1172
 
1164
1173
  manufacturer = Manufacturer.objects.first()
@@ -1222,10 +1231,12 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1222
1231
  ),
1223
1232
  )
1224
1233
 
1234
+ device_bay = DeviceBay.objects.create(device=devices[0], name="Device Bay 1")
1235
+
1225
1236
  cls.relationships = (
1226
1237
  Relationship(
1227
- name="BGP Router-ID",
1228
- slug="router-id",
1238
+ label="BGP Router-ID",
1239
+ key="router_id",
1229
1240
  type=RelationshipTypeChoices.TYPE_ONE_TO_ONE,
1230
1241
  source_type=ContentType.objects.get_for_model(Device),
1231
1242
  source_label="BGP Router ID",
@@ -1236,14 +1247,16 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1236
1247
  for relationship in cls.relationships:
1237
1248
  relationship.validated_save()
1238
1249
 
1250
+ cls.ipaddr_status = Status.objects.get_for_model(IPAddress).first()
1251
+ cls.prefix_status = Status.objects.get_for_model(Prefix).first()
1239
1252
  namespace = Namespace.objects.first()
1240
- Prefix.objects.create(prefix="1.1.1.1/24", namespace=namespace)
1241
- Prefix.objects.create(prefix="2.2.2.2/24", namespace=namespace)
1242
- Prefix.objects.create(prefix="3.3.3.3/24", namespace=namespace)
1253
+ Prefix.objects.create(prefix="1.1.1.1/24", namespace=namespace, status=cls.prefix_status)
1254
+ Prefix.objects.create(prefix="2.2.2.2/24", namespace=namespace, status=cls.prefix_status)
1255
+ Prefix.objects.create(prefix="3.3.3.3/24", namespace=namespace, status=cls.prefix_status)
1243
1256
  ipaddresses = (
1244
- IPAddress.objects.create(address="1.1.1.1/32", namespace=namespace),
1245
- IPAddress.objects.create(address="2.2.2.2/32", namespace=namespace),
1246
- IPAddress.objects.create(address="3.3.3.3/32", namespace=namespace),
1257
+ IPAddress.objects.create(address="1.1.1.1/32", namespace=namespace, status=cls.ipaddr_status),
1258
+ IPAddress.objects.create(address="2.2.2.2/32", namespace=namespace, status=cls.ipaddr_status),
1259
+ IPAddress.objects.create(address="3.3.3.3/32", namespace=namespace, status=cls.ipaddr_status),
1247
1260
  )
1248
1261
 
1249
1262
  for device, ipaddress in zip(devices, ipaddresses):
@@ -1279,10 +1292,11 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1279
1292
  }
1280
1293
 
1281
1294
  cls.csv_data = (
1282
- "role,manufacturer,device_type,status,name,location,rack_group,rack,position,face,secrets_group",
1283
- f"{deviceroles[0].name},{manufacturer.name},Device Type 1,{statuses[0].name},Device 4,{locations[0].name},Rack Group 1,Rack 1,10,front,",
1284
- f"{deviceroles[0].name},{manufacturer.name},Device Type 1,{statuses[0].name},Device 5,{locations[0].name},Rack Group 1,Rack 1,20,front,",
1285
- f"{deviceroles[0].name},{manufacturer.name},Device Type 1,{statuses[0].name},Device 6,{locations[0].name},Rack Group 1,Rack 1,30,front,Secrets Group 2",
1295
+ "role,device_type,status,name,location,rack,position,face,secrets_group,parent_bay",
1296
+ f"{deviceroles[0].name},{devicetypes[0].natural_key_slug},{statuses[0].name},Device 4,{locations[0].name},{racks[0].natural_key_slug},10,front,",
1297
+ f"{deviceroles[0].name},{devicetypes[0].natural_key_slug},{statuses[0].name},Device 5,{locations[0].name},{racks[0].natural_key_slug},20,front,",
1298
+ f"{deviceroles[0].name},{devicetypes[0].natural_key_slug},{statuses[0].name},Device 6,{locations[0].name},{racks[0].natural_key_slug},30,front,Secrets Group 2",
1299
+ f"{deviceroles[1].name},{devicetypes[1].natural_key_slug},{statuses[0].name},Child Device,{locations[0].name},,,,,{device_bay.natural_key_slug}",
1286
1300
  )
1287
1301
 
1288
1302
  cls.bulk_edit_data = {
@@ -1347,9 +1361,10 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1347
1361
  def test_device_interfaces(self):
1348
1362
  device = Device.objects.first()
1349
1363
 
1350
- Interface.objects.create(device=device, name="Interface 1")
1351
- Interface.objects.create(device=device, name="Interface 2")
1352
- Interface.objects.create(device=device, name="Interface 3")
1364
+ intf_status = Status.objects.get_for_model(Interface).first()
1365
+ Interface.objects.create(device=device, name="Interface 1", status=intf_status)
1366
+ Interface.objects.create(device=device, name="Interface 2", status=intf_status)
1367
+ Interface.objects.create(device=device, name="Interface 3", status=intf_status)
1353
1368
 
1354
1369
  url = reverse("dcim:device_interfaces", kwargs={"pk": device.pk})
1355
1370
  self.assertHttpStatus(self.client.get(url), 200)
@@ -1400,7 +1415,7 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1400
1415
  def test_device_devicebays(self):
1401
1416
  device = Device.objects.first()
1402
1417
 
1403
- DeviceBay.objects.create(device=device, name="Device Bay 1")
1418
+ # Device Bay 1 was already created in setUpTestData()
1404
1419
  DeviceBay.objects.create(device=device, name="Device Bay 2")
1405
1420
  DeviceBay.objects.create(device=device, name="Device Bay 3")
1406
1421
 
@@ -1425,16 +1440,16 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1425
1440
 
1426
1441
  # Create an interface and assign an IP to it.
1427
1442
  device = Device.objects.first()
1428
- interface = Interface.objects.create(device=device, name="Interface 1")
1443
+ intf_status = Status.objects.get_for_model(Interface).first()
1444
+ interface = Interface.objects.create(device=device, name="Interface 1", status=intf_status)
1429
1445
  namespace = Namespace.objects.first()
1430
- Prefix.objects.create(prefix="1.2.3.0/24", namespace=namespace)
1431
- ip_address = IPAddress.objects.create(address="1.2.3.4/32", namespace=namespace)
1446
+ Prefix.objects.create(prefix="1.2.3.0/24", namespace=namespace, status=self.prefix_status)
1447
+ ip_address = IPAddress.objects.create(address="1.2.3.4/32", namespace=namespace, status=self.ipaddr_status)
1432
1448
  interface.ip_addresses.add(ip_address)
1433
1449
 
1434
1450
  # Dupe the form data and populated primary_ip4 w/ ip_address
1435
1451
  form_data = self.form_data.copy()
1436
1452
  form_data["primary_ip4"] = ip_address.pk
1437
-
1438
1453
  # Assert that update succeeds.
1439
1454
  request = {
1440
1455
  "path": self._get_url("edit", device),
@@ -1533,9 +1548,9 @@ class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1533
1548
 
1534
1549
  cls.csv_data = (
1535
1550
  "device,name",
1536
- "Device 1,Console Port 4",
1537
- "Device 1,Console Port 5",
1538
- "Device 1,Console Port 6",
1551
+ f"{device.natural_key_slug},Console Port 4",
1552
+ f"{device.natural_key_slug},Console Port 5",
1553
+ f"{device.natural_key_slug},Console Port 6",
1539
1554
  )
1540
1555
 
1541
1556
 
@@ -1579,9 +1594,9 @@ class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1579
1594
 
1580
1595
  cls.csv_data = (
1581
1596
  "device,name",
1582
- "Device 1,Console Server Port 4",
1583
- "Device 1,Console Server Port 5",
1584
- "Device 1,Console Server Port 6",
1597
+ f"{device.natural_key_slug},Console Server Port 4",
1598
+ f"{device.natural_key_slug},Console Server Port 5",
1599
+ f"{device.natural_key_slug},Console Server Port 6",
1585
1600
  )
1586
1601
 
1587
1602
 
@@ -1630,9 +1645,9 @@ class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1630
1645
 
1631
1646
  cls.csv_data = (
1632
1647
  "device,name",
1633
- "Device 1,Power Port 4",
1634
- "Device 1,Power Port 5",
1635
- "Device 1,Power Port 6",
1648
+ f"{device.natural_key_slug},Power Port 4",
1649
+ f"{device.natural_key_slug},Power Port 5",
1650
+ f"{device.natural_key_slug},Power Port 6",
1636
1651
  )
1637
1652
 
1638
1653
 
@@ -1695,9 +1710,9 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase):
1695
1710
 
1696
1711
  cls.csv_data = (
1697
1712
  "device,name",
1698
- "Device 1,Power Outlet 4",
1699
- "Device 1,Power Outlet 5",
1700
- "Device 1,Power Outlet 6",
1713
+ f"{device.natural_key_slug},Power Outlet 4",
1714
+ f"{device.natural_key_slug},Power Outlet 5",
1715
+ f"{device.natural_key_slug},Power Outlet 6",
1701
1716
  )
1702
1717
 
1703
1718
 
@@ -1712,21 +1727,26 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
1712
1727
  status_active = statuses[0]
1713
1728
 
1714
1729
  interfaces = (
1715
- Interface.objects.create(device=device, name="Interface 1"),
1716
- Interface.objects.create(device=device, name="Interface 2"),
1717
- Interface.objects.create(device=device, name="Interface 3"),
1718
- Interface.objects.create(device=device, name="LAG", type=InterfaceTypeChoices.TYPE_LAG),
1719
- Interface.objects.create(device=device, name="BRIDGE", type=InterfaceTypeChoices.TYPE_BRIDGE),
1730
+ Interface.objects.create(device=device, name="Interface 1", status=status_active),
1731
+ Interface.objects.create(device=device, name="Interface 2", status=status_active),
1732
+ Interface.objects.create(device=device, name="Interface 3", status=status_active),
1733
+ Interface.objects.create(
1734
+ device=device, name="LAG", status=status_active, type=InterfaceTypeChoices.TYPE_LAG
1735
+ ),
1736
+ Interface.objects.create(
1737
+ device=device, name="BRIDGE", status=status_active, type=InterfaceTypeChoices.TYPE_BRIDGE
1738
+ ),
1720
1739
  )
1721
1740
  # Required by ViewTestCases.DeviceComponentViewTestCase.test_bulk_rename
1722
1741
  cls.selected_objects = interfaces
1723
1742
  cls.selected_objects_parent_name = device.name
1724
1743
 
1744
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1725
1745
  vlans = (
1726
- VLAN.objects.create(vid=1, name="VLAN1", location=device.location),
1727
- VLAN.objects.create(vid=101, name="VLAN101", location=device.location),
1728
- VLAN.objects.create(vid=102, name="VLAN102", location=device.location),
1729
- VLAN.objects.create(vid=103, name="VLAN103", location=device.location),
1746
+ VLAN.objects.create(vid=1, name="VLAN1", location=device.location, status=vlan_status),
1747
+ VLAN.objects.create(vid=101, name="VLAN101", location=device.location, status=vlan_status),
1748
+ VLAN.objects.create(vid=102, name="VLAN102", location=device.location, status=vlan_status),
1749
+ VLAN.objects.create(vid=103, name="VLAN103", location=device.location, status=vlan_status),
1730
1750
  )
1731
1751
 
1732
1752
  cls.form_data = {
@@ -1795,9 +1815,9 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
1795
1815
 
1796
1816
  cls.csv_data = (
1797
1817
  "device,name,type,status",
1798
- f"Device 1,Interface 4,1000base-t,{statuses[0].name}",
1799
- f"Device 1,Interface 5,1000base-t,{statuses[0].name}",
1800
- f"Device 1,Interface 6,1000base-t,{statuses[0].name}",
1818
+ f"{device.natural_key_slug},Interface 4,1000base-t,{statuses[0].name}",
1819
+ f"{device.natural_key_slug},Interface 5,1000base-t,{statuses[0].name}",
1820
+ f"{device.natural_key_slug},Interface 6,1000base-t,{statuses[0].name}",
1801
1821
  )
1802
1822
 
1803
1823
 
@@ -1853,9 +1873,9 @@ class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1853
1873
 
1854
1874
  cls.csv_data = (
1855
1875
  "device,name,type,rear_port,rear_port_position",
1856
- "Device 1,Front Port 4,8p8c,Rear Port 4,1",
1857
- "Device 1,Front Port 5,8p8c,Rear Port 5,1",
1858
- "Device 1,Front Port 6,8p8c,Rear Port 6,1",
1876
+ f"{device.natural_key_slug},Front Port 4,8p8c,{rearports[3].natural_key_slug},1",
1877
+ f"{device.natural_key_slug},Front Port 5,8p8c,{rearports[4].natural_key_slug},1",
1878
+ f"{device.natural_key_slug},Front Port 6,8p8c,{rearports[5].natural_key_slug},1",
1859
1879
  )
1860
1880
 
1861
1881
  @unittest.skip("No DeviceBulkAddFrontPortView exists at present")
@@ -1904,9 +1924,9 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1904
1924
 
1905
1925
  cls.csv_data = (
1906
1926
  "device,name,type,positions",
1907
- "Device 1,Rear Port 4,8p8c,1",
1908
- "Device 1,Rear Port 5,8p8c,1",
1909
- "Device 1,Rear Port 6,8p8c,1",
1927
+ f"{device.natural_key_slug},Rear Port 4,8p8c,1",
1928
+ f"{device.natural_key_slug},Rear Port 5,8p8c,1",
1929
+ f"{device.natural_key_slug},Rear Port 6,8p8c,1",
1910
1930
  )
1911
1931
 
1912
1932
 
@@ -1949,9 +1969,9 @@ class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
1949
1969
 
1950
1970
  cls.csv_data = (
1951
1971
  "device,name",
1952
- "Device 1,Device Bay 4",
1953
- "Device 1,Device Bay 5",
1954
- "Device 1,Device Bay 6",
1972
+ f"{device.natural_key_slug},Device Bay 4",
1973
+ f"{device.natural_key_slug},Device Bay 5",
1974
+ f"{device.natural_key_slug},Device Bay 6",
1955
1975
  )
1956
1976
 
1957
1977
 
@@ -2004,9 +2024,9 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
2004
2024
 
2005
2025
  cls.csv_data = (
2006
2026
  "device,name",
2007
- "Device 1,Inventory Item 4",
2008
- "Device 1,Inventory Item 5",
2009
- "Device 1,Inventory Item 6",
2027
+ f"{device.natural_key_slug},Inventory Item 4",
2028
+ f"{device.natural_key_slug},Inventory Item 5",
2029
+ f"{device.natural_key_slug},Inventory Item 6",
2010
2030
  )
2011
2031
 
2012
2032
 
@@ -2030,6 +2050,7 @@ class CableTestCase(
2030
2050
  manufacturer = Manufacturer.objects.first()
2031
2051
  devicetype = DeviceType.objects.create(model="Device Type 1", manufacturer=manufacturer)
2032
2052
  devicerole = Role.objects.get_for_model(Device).first()
2053
+ devicestatus = Status.objects.get_for_model(Device).first()
2033
2054
 
2034
2055
  devices = (
2035
2056
  Device.objects.create(
@@ -2037,108 +2058,128 @@ class CableTestCase(
2037
2058
  location=location,
2038
2059
  device_type=devicetype,
2039
2060
  role=devicerole,
2061
+ status=devicestatus,
2040
2062
  ),
2041
2063
  Device.objects.create(
2042
2064
  name="Device 2",
2043
2065
  location=location,
2044
2066
  device_type=devicetype,
2045
2067
  role=devicerole,
2068
+ status=devicestatus,
2046
2069
  ),
2047
2070
  Device.objects.create(
2048
2071
  name="Device 3",
2049
2072
  location=location,
2050
2073
  device_type=devicetype,
2051
2074
  role=devicerole,
2075
+ status=devicestatus,
2052
2076
  ),
2053
2077
  Device.objects.create(
2054
2078
  name="Device 4",
2055
2079
  location=location,
2056
2080
  device_type=devicetype,
2057
2081
  role=devicerole,
2082
+ status=devicestatus,
2058
2083
  ),
2059
2084
  )
2060
2085
 
2086
+ interface_status = Status.objects.get_for_model(Interface).first()
2061
2087
  interfaces = (
2062
2088
  Interface.objects.create(
2063
2089
  device=devices[0],
2064
2090
  name="Interface 1",
2065
2091
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2092
+ status=interface_status,
2066
2093
  ),
2067
2094
  Interface.objects.create(
2068
2095
  device=devices[0],
2069
2096
  name="Interface 2",
2070
2097
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2098
+ status=interface_status,
2071
2099
  ),
2072
2100
  Interface.objects.create(
2073
2101
  device=devices[0],
2074
2102
  name="Interface 3",
2075
2103
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2104
+ status=interface_status,
2076
2105
  ),
2077
2106
  Interface.objects.create(
2078
2107
  device=devices[1],
2079
2108
  name="Interface 1",
2080
2109
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2110
+ status=interface_status,
2081
2111
  ),
2082
2112
  Interface.objects.create(
2083
2113
  device=devices[1],
2084
2114
  name="Interface 2",
2085
2115
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2116
+ status=interface_status,
2086
2117
  ),
2087
2118
  Interface.objects.create(
2088
2119
  device=devices[1],
2089
2120
  name="Interface 3",
2090
2121
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2122
+ status=interface_status,
2091
2123
  ),
2092
2124
  Interface.objects.create(
2093
2125
  device=devices[2],
2094
2126
  name="Interface 1",
2095
2127
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2128
+ status=interface_status,
2096
2129
  ),
2097
2130
  Interface.objects.create(
2098
2131
  device=devices[2],
2099
2132
  name="Interface 2",
2100
2133
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2134
+ status=interface_status,
2101
2135
  ),
2102
2136
  Interface.objects.create(
2103
2137
  device=devices[2],
2104
2138
  name="Interface 3",
2105
2139
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2140
+ status=interface_status,
2106
2141
  ),
2107
2142
  Interface.objects.create(
2108
2143
  device=devices[3],
2109
2144
  name="Interface 1",
2110
2145
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2146
+ status=interface_status,
2111
2147
  ),
2112
2148
  Interface.objects.create(
2113
2149
  device=devices[3],
2114
2150
  name="Interface 2",
2115
2151
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2152
+ status=interface_status,
2116
2153
  ),
2117
2154
  Interface.objects.create(
2118
2155
  device=devices[3],
2119
2156
  name="Interface 3",
2120
2157
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
2158
+ status=interface_status,
2121
2159
  ),
2122
2160
  )
2123
2161
 
2162
+ statuses = Status.objects.get_for_model(Cable)
2163
+
2124
2164
  Cable.objects.create(
2125
2165
  termination_a=interfaces[0],
2126
2166
  termination_b=interfaces[3],
2127
2167
  type=CableTypeChoices.TYPE_CAT6,
2168
+ status=statuses[0],
2128
2169
  )
2129
2170
  Cable.objects.create(
2130
2171
  termination_a=interfaces[1],
2131
2172
  termination_b=interfaces[4],
2132
2173
  type=CableTypeChoices.TYPE_CAT6,
2174
+ status=statuses[0],
2133
2175
  )
2134
2176
  Cable.objects.create(
2135
2177
  termination_a=interfaces[2],
2136
2178
  termination_b=interfaces[5],
2137
2179
  type=CableTypeChoices.TYPE_CAT6,
2180
+ status=statuses[0],
2138
2181
  )
2139
2182
 
2140
- statuses = Status.objects.get_for_model(Cable)
2141
-
2142
2183
  # interface_ct = ContentType.objects.get_for_model(Interface)
2143
2184
  cls.form_data = {
2144
2185
  # Changing terminations not supported when editing an existing Cable
@@ -2157,10 +2198,10 @@ class CableTestCase(
2157
2198
  }
2158
2199
 
2159
2200
  cls.csv_data = (
2160
- "side_a_device,side_a_type,side_a_name,side_b_device,side_b_type,side_b_name,status",
2161
- f"Device 3,dcim.interface,Interface 1,Device 4,dcim.interface,Interface 1,{statuses[0].name}",
2162
- f"Device 3,dcim.interface,Interface 2,Device 4,dcim.interface,Interface 2,{statuses[0].name}",
2163
- f"Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3,{statuses[0].name}",
2201
+ "termination_a_id,termination_a_type,termination_b_id,termination_b_type,status",
2202
+ f"{interfaces[6].id},dcim.interface,{interfaces[9].id},dcim.interface,{statuses[0].name}",
2203
+ f"{interfaces[7].id},dcim.interface,{interfaces[10].id},dcim.interface,{statuses[0].name}",
2204
+ f"{interfaces[8].id},dcim.interface,{interfaces[11].id},dcim.interface,{statuses[0].name}",
2164
2205
  )
2165
2206
 
2166
2207
  cls.bulk_edit_data = {
@@ -2179,14 +2220,18 @@ class CableTestCase(
2179
2220
  location = Location.objects.first()
2180
2221
  device = Device.objects.first()
2181
2222
 
2223
+ interface_status = Status.objects.get_for_model(Interface).first()
2182
2224
  interfaces = [
2183
- Interface.objects.create(device=device, name="eth0"),
2184
- Interface.objects.create(device=device, name="eth1"),
2225
+ Interface.objects.create(device=device, name="eth0", status=interface_status),
2226
+ Interface.objects.create(device=device, name="eth1", status=interface_status),
2185
2227
  ]
2186
2228
 
2187
2229
  provider = Provider.objects.first()
2188
2230
  circuittype = CircuitType.objects.first()
2189
- circuit = Circuit.objects.create(cid="Circuit 1", provider=provider, circuit_type=circuittype)
2231
+ circuit_status = Status.objects.get_for_model(Circuit).first()
2232
+ circuit = Circuit.objects.create(
2233
+ cid="Circuit 1", provider=provider, circuit_type=circuittype, status=circuit_status
2234
+ )
2190
2235
 
2191
2236
  circuit_terminations = [
2192
2237
  CircuitTermination.objects.create(
@@ -2277,21 +2322,6 @@ class ConsoleConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2277
2322
  Cable.objects.create(termination_a=consoleports[1], termination_b=serverports[1], status=status_connected)
2278
2323
  Cable.objects.create(termination_a=consoleports[2], termination_b=rearport, status=status_connected)
2279
2324
 
2280
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2281
- def test_queryset_to_csv(self):
2282
- """This view has a custom queryset_to_csv() implementation."""
2283
- response = self.client.get(f"{self._get_url('list')}?export")
2284
- self.assertHttpStatus(response, 200)
2285
- self.assertEqual(response.get("Content-Type"), "text/csv")
2286
- self.assertEqual(
2287
- """\
2288
- device,console_port,console_server,port,reachable
2289
- Device 1,Console Port 1,Device 2,Console Server Port 1,True
2290
- Device 1,Console Port 2,Device 2,Console Server Port 2,True
2291
- Device 1,Console Port 3,,,False""",
2292
- response.content.decode(response.charset),
2293
- )
2294
-
2295
2325
 
2296
2326
  class PowerConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2297
2327
  """
@@ -2329,7 +2359,8 @@ class PowerConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2329
2359
  )
2330
2360
 
2331
2361
  powerpanel = PowerPanel.objects.create(location=location, name="Power Panel 1")
2332
- powerfeed = PowerFeed.objects.create(power_panel=powerpanel, name="Power Feed 1")
2362
+ pf_status = Status.objects.get_for_model(PowerFeed).first()
2363
+ powerfeed = PowerFeed.objects.create(power_panel=powerpanel, name="Power Feed 1", status=pf_status)
2333
2364
 
2334
2365
  status_connected = Status.objects.get(name="Connected")
2335
2366
 
@@ -2338,21 +2369,6 @@ class PowerConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2338
2369
  Cable.objects.create(termination_a=powerports[0], termination_b=poweroutlets[0], status=status_connected)
2339
2370
  Cable.objects.create(termination_a=powerports[1], termination_b=poweroutlets[1], status=status_connected)
2340
2371
 
2341
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2342
- def test_queryset_to_csv(self):
2343
- """This view has a custom queryset_to_csv() implementation."""
2344
- response = self.client.get(f"{self._get_url('list')}?export")
2345
- self.assertHttpStatus(response, 200)
2346
- self.assertEqual(response.get("Content-Type"), "text/csv")
2347
- self.assertEqual(
2348
- """\
2349
- device,power_port,pdu,outlet,reachable
2350
- Device 1,Power Port 1,Device 2,Power Outlet 1,True
2351
- Device 1,Power Port 2,Device 2,Power Outlet 2,True
2352
- Device 1,Power Port 3,,Power Feed 1,True""",
2353
- response.content.decode(response.charset),
2354
- )
2355
-
2356
2372
 
2357
2373
  class InterfaceConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2358
2374
  """
@@ -2378,20 +2394,30 @@ class InterfaceConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2378
2394
  device_1 = create_test_device("Device 1")
2379
2395
  device_2 = create_test_device("Device 2")
2380
2396
 
2397
+ interface_status = Status.objects.get_for_model(Interface).first()
2381
2398
  cls.interfaces = (
2382
- Interface.objects.create(device=device_1, name="Interface 1", type=InterfaceTypeChoices.TYPE_1GE_SFP),
2383
- Interface.objects.create(device=device_1, name="Interface 2", type=InterfaceTypeChoices.TYPE_1GE_SFP),
2384
- Interface.objects.create(device=device_1, name="Interface 3", type=InterfaceTypeChoices.TYPE_1GE_SFP),
2399
+ Interface.objects.create(
2400
+ device=device_1, name="Interface 1", type=InterfaceTypeChoices.TYPE_1GE_SFP, status=interface_status
2401
+ ),
2402
+ Interface.objects.create(
2403
+ device=device_1, name="Interface 2", type=InterfaceTypeChoices.TYPE_1GE_SFP, status=interface_status
2404
+ ),
2405
+ Interface.objects.create(
2406
+ device=device_1, name="Interface 3", type=InterfaceTypeChoices.TYPE_1GE_SFP, status=interface_status
2407
+ ),
2385
2408
  )
2386
2409
 
2387
2410
  cls.device_2_interface = Interface.objects.create(
2388
- device=device_2, name="Interface 1", type=InterfaceTypeChoices.TYPE_1GE_SFP
2411
+ device=device_2, name="Interface 1", type=InterfaceTypeChoices.TYPE_1GE_SFP, status=interface_status
2389
2412
  )
2390
2413
  rearport = RearPort.objects.create(device=device_2, type=PortTypeChoices.TYPE_8P8C)
2391
2414
 
2392
2415
  provider = Provider.objects.first()
2393
2416
  circuittype = CircuitType.objects.first()
2394
- circuit = Circuit.objects.create(cid="Circuit 1", provider=provider, circuit_type=circuittype)
2417
+ circuit_status = Status.objects.get_for_model(Circuit).first()
2418
+ circuit = Circuit.objects.create(
2419
+ cid="Circuit 1", provider=provider, circuit_type=circuittype, status=circuit_status
2420
+ )
2395
2421
  circuittermination = CircuitTermination.objects.create(
2396
2422
  circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A, location=location
2397
2423
  )
@@ -2402,21 +2428,6 @@ class InterfaceConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2402
2428
  Cable.objects.create(termination_a=cls.interfaces[1], termination_b=circuittermination, status=connected)
2403
2429
  Cable.objects.create(termination_a=cls.interfaces[2], termination_b=rearport, status=connected)
2404
2430
 
2405
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2406
- def test_queryset_to_csv(self):
2407
- """This view has a custom queryset_to_csv() implementation."""
2408
- response = self.client.get(f"{self._get_url('list')}?export")
2409
- self.assertHttpStatus(response, 200)
2410
- self.assertEqual(response.get("Content-Type"), "text/csv")
2411
- self.assertEqual(
2412
- """\
2413
- device_a,interface_a,device_b,interface_b,reachable
2414
- Device 1,Interface 1,Device 2,Interface 1,True
2415
- Device 1,Interface 2,,,True
2416
- Device 1,Interface 3,,,False""",
2417
- response.content.decode(response.charset),
2418
- )
2419
-
2420
2431
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2421
2432
  def test_list_objects_filtered(self):
2422
2433
  """Extend base ListObjectsViewTestCase to filter based on *both ends* of a connection."""
@@ -2465,11 +2476,13 @@ class VirtualChassisTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2465
2476
  manufacturer = Manufacturer.objects.first()
2466
2477
  device_type = DeviceType.objects.create(manufacturer=manufacturer, model="Device Type 1", slug="device-type-1")
2467
2478
  device_role = Role.objects.get_for_model(Device).first()
2479
+ device_status = Status.objects.get_for_model(Device).first()
2468
2480
 
2469
2481
  cls.devices = [
2470
2482
  Device.objects.create(
2471
2483
  device_type=device_type,
2472
2484
  role=device_role,
2485
+ status=device_status,
2473
2486
  name=f"Device {num}",
2474
2487
  location=location,
2475
2488
  )
@@ -2502,9 +2515,9 @@ class VirtualChassisTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2502
2515
 
2503
2516
  cls.csv_data = (
2504
2517
  "name,domain,master",
2505
- "VC4,Domain 4,Device 10",
2506
- "VC5,Domain 5,Device 11",
2507
- "VC6,Domain 6,Device 12",
2518
+ f"VC4,Domain 4,{cls.devices[9].natural_key_slug}",
2519
+ f"VC5,Domain 5,{cls.devices[10].natural_key_slug}",
2520
+ f"VC6,Domain 6,{cls.devices[11].natural_key_slug}",
2508
2521
  )
2509
2522
 
2510
2523
  cls.bulk_edit_data = {
@@ -2518,8 +2531,9 @@ class VirtualChassisTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2518
2531
  """
2519
2532
  self.user.is_superuser = True
2520
2533
  self.user.save()
2521
- Interface.objects.create(device=self.devices[0], name="eth0")
2522
- Interface.objects.create(device=self.devices[0], name="eth1")
2534
+ interface_status = Status.objects.get_for_model(Interface).first()
2535
+ Interface.objects.create(device=self.devices[0], name="eth0", status=interface_status)
2536
+ Interface.objects.create(device=self.devices[0], name="eth1", status=interface_status)
2523
2537
  response = self.client.get(reverse("dcim:device_interfaces", kwargs={"pk": self.devices[0].pk}))
2524
2538
  self.assertIn("<th >Device</th>", str(response.content))
2525
2539
 
@@ -2530,8 +2544,9 @@ class VirtualChassisTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2530
2544
  """
2531
2545
  self.user.is_superuser = True
2532
2546
  self.user.save()
2533
- Interface.objects.create(device=self.devices[1], name="eth2")
2534
- Interface.objects.create(device=self.devices[1], name="eth3")
2547
+ interface_status = Status.objects.get_for_model(Interface).first()
2548
+ Interface.objects.create(device=self.devices[1], name="eth2", status=interface_status)
2549
+ Interface.objects.create(device=self.devices[1], name="eth3", status=interface_status)
2535
2550
  response = self.client.get(reverse("dcim:device_interfaces", kwargs={"pk": self.devices[1].pk}))
2536
2551
  self.assertNotIn("<th >Device</th>", str(response.content))
2537
2552
  # Sanity check:
@@ -2562,9 +2577,9 @@ class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2562
2577
 
2563
2578
  cls.csv_data = (
2564
2579
  "location,rack_group,name",
2565
- f"{locations[0].name},Rack Group 1,Power Panel 4",
2566
- f"{locations[0].name},Rack Group 1,Power Panel 5",
2567
- f"{locations[0].name},Rack Group 1,Power Panel 6",
2580
+ f"{locations[0].natural_key_slug},{rackgroups[0].natural_key_slug},Power Panel 4",
2581
+ f"{locations[0].natural_key_slug},{rackgroups[0].natural_key_slug},Power Panel 5",
2582
+ f"{locations[0].natural_key_slug},{rackgroups[0].natural_key_slug},Power Panel 6",
2568
2583
  )
2569
2584
 
2570
2585
  cls.bulk_edit_data = {
@@ -2591,22 +2606,27 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2591
2606
  # Assign power panels generated to the class object for use later.
2592
2607
  cls.powerpanels = powerpanels
2593
2608
 
2609
+ rack_status = Status.objects.get_for_model(Rack).first()
2594
2610
  racks = (
2595
- Rack.objects.create(location=location, name="Rack 1"),
2596
- Rack.objects.create(location=location, name="Rack 2"),
2611
+ Rack.objects.create(location=location, name="Rack 1", status=rack_status),
2612
+ Rack.objects.create(location=location, name="Rack 2", status=rack_status),
2597
2613
  )
2598
2614
 
2599
- powerfeed_1 = PowerFeed.objects.create(name="Power Feed 1", power_panel=powerpanels[0], rack=racks[0])
2600
- powerfeed_2 = PowerFeed.objects.create(name="Power Feed 2", power_panel=powerpanels[0], rack=racks[0])
2601
- PowerFeed.objects.create(name="Power Feed 3", power_panel=powerpanels[0], rack=racks[0])
2602
-
2603
- # Assign power feeds for the tests later
2604
- cls.powerfeeds = (powerfeed_1, powerfeed_2)
2605
-
2606
2615
  statuses = Status.objects.get_for_model(PowerFeed)
2607
2616
  cls.status = statuses
2608
2617
  status_planned = statuses[0]
2609
2618
 
2619
+ powerfeed_1 = PowerFeed.objects.create(
2620
+ name="Power Feed 1", power_panel=powerpanels[0], rack=racks[0], status=status_planned
2621
+ )
2622
+ powerfeed_2 = PowerFeed.objects.create(
2623
+ name="Power Feed 2", power_panel=powerpanels[0], rack=racks[0], status=status_planned
2624
+ )
2625
+ PowerFeed.objects.create(name="Power Feed 3", power_panel=powerpanels[0], rack=racks[0], status=status_planned)
2626
+
2627
+ # Assign power feeds for the tests later
2628
+ cls.powerfeeds = (powerfeed_1, powerfeed_2)
2629
+
2610
2630
  cls.form_data = {
2611
2631
  "name": "Power Feed X",
2612
2632
  "power_panel": powerpanels[1].pk,
@@ -2623,10 +2643,10 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2623
2643
  }
2624
2644
 
2625
2645
  cls.csv_data = (
2626
- "location,power_panel,name,voltage,amperage,max_utilization,status",
2627
- f"{location.name},Power Panel 1,Power Feed 4,120,20,80,{statuses[0].name}",
2628
- f"{location.name},Power Panel 1,Power Feed 5,120,20,80,{statuses[0].name}",
2629
- f"{location.name},Power Panel 1,Power Feed 6,120,20,80,{statuses[1].name}",
2646
+ "power_panel,name,voltage,amperage,max_utilization,status",
2647
+ f"{powerpanels[0].natural_key_slug},Power Feed 4,120,20,80,{statuses[0].name}",
2648
+ f"{powerpanels[0].natural_key_slug},Power Feed 5,120,20,80,{statuses[0].name}",
2649
+ f"{powerpanels[0].natural_key_slug},Power Feed 6,120,20,80,{statuses[1].name}",
2630
2650
  )
2631
2651
 
2632
2652
  cls.bulk_edit_data = {
@@ -2648,9 +2668,11 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2648
2668
  manufacturer = Manufacturer.objects.first()
2649
2669
  device_type = DeviceType.objects.create(manufacturer=manufacturer, model="Device Type 1", slug="device-type-1")
2650
2670
  device_role = Role.objects.get_for_model(Device).first()
2671
+ device_status = Status.objects.get_for_model(Device).first()
2651
2672
  device = Device.objects.create(
2652
2673
  device_type=device_type,
2653
2674
  role=device_role,
2675
+ status=device_status,
2654
2676
  name="Device1",
2655
2677
  location=self.location,
2656
2678
  )