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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (780) hide show
  1. nautobot/apps/api.py +6 -8
  2. nautobot/apps/forms.py +0 -2
  3. nautobot/apps/ui.py +0 -8
  4. nautobot/circuits/api/serializers.py +9 -117
  5. nautobot/circuits/api/urls.py +1 -1
  6. nautobot/circuits/api/views.py +0 -1
  7. nautobot/circuits/forms.py +0 -65
  8. nautobot/circuits/migrations/0014_related_name_changes.py +1 -1
  9. nautobot/circuits/migrations/0016_tagsfield.py +34 -0
  10. nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
  11. nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
  12. nautobot/circuits/models.py +3 -87
  13. nautobot/circuits/navigation.py +14 -69
  14. nautobot/circuits/signals.py +0 -2
  15. nautobot/circuits/tables.py +39 -1
  16. nautobot/circuits/tests/integration/test_relationships.py +9 -9
  17. nautobot/circuits/tests/test_api.py +4 -8
  18. nautobot/circuits/tests/test_filters.py +10 -4
  19. nautobot/circuits/tests/test_models.py +5 -1
  20. nautobot/circuits/tests/test_views.py +27 -5
  21. nautobot/circuits/views.py +18 -10
  22. nautobot/core/api/__init__.py +8 -2
  23. nautobot/core/api/fields.py +15 -6
  24. nautobot/core/api/filter_backends.py +3 -2
  25. nautobot/core/api/metadata.py +237 -30
  26. nautobot/core/api/mixins.py +94 -0
  27. nautobot/core/api/pagination.py +4 -0
  28. nautobot/core/api/parsers.py +154 -0
  29. nautobot/core/api/renderers.py +153 -2
  30. nautobot/core/api/schema.py +46 -2
  31. nautobot/core/api/serializers.py +377 -35
  32. nautobot/core/api/urls.py +11 -3
  33. nautobot/core/api/utils.py +174 -2
  34. nautobot/core/api/versioning.py +32 -10
  35. nautobot/core/api/views.py +266 -72
  36. nautobot/core/apps/__init__.py +138 -220
  37. nautobot/core/celery/__init__.py +112 -41
  38. nautobot/core/celery/backends.py +19 -12
  39. nautobot/core/celery/control.py +46 -0
  40. nautobot/core/celery/encoders.py +53 -0
  41. nautobot/core/celery/log.py +38 -0
  42. nautobot/core/celery/schedulers.py +23 -4
  43. nautobot/core/celery/task.py +1 -16
  44. nautobot/core/checks.py +0 -27
  45. nautobot/core/choices.py +0 -113
  46. nautobot/core/{cli.py → cli/__init__.py} +1 -1
  47. nautobot/core/cli/__main__.py +3 -0
  48. nautobot/core/constants.py +0 -24
  49. nautobot/core/context_processors.py +12 -0
  50. nautobot/core/filters.py +2 -2
  51. nautobot/core/forms/__init__.py +0 -4
  52. nautobot/core/forms/fields.py +38 -65
  53. nautobot/core/forms/forms.py +4 -1
  54. nautobot/core/forms/utils.py +0 -52
  55. nautobot/core/graphql/schema.py +4 -27
  56. nautobot/core/jobs/__init__.py +75 -0
  57. nautobot/core/management/commands/build_ui.py +255 -0
  58. nautobot/core/management/commands/generate_test_data.py +3 -2
  59. nautobot/core/management/commands/post_upgrade.py +24 -24
  60. nautobot/core/models/__init__.py +26 -1
  61. nautobot/core/models/fields.py +24 -5
  62. nautobot/core/models/generics.py +2 -42
  63. nautobot/core/models/managers.py +5 -0
  64. nautobot/core/models/name_color_content_types.py +0 -14
  65. nautobot/core/models/tree_queries.py +14 -4
  66. nautobot/core/models/utils.py +5 -6
  67. nautobot/core/models/validators.py +17 -8
  68. nautobot/core/releases.py +8 -10
  69. nautobot/core/settings.py +80 -42
  70. nautobot/core/tables.py +5 -5
  71. nautobot/core/tasks.py +4 -7
  72. nautobot/core/templates/base.html +1 -49
  73. nautobot/core/templates/base_django.html +49 -0
  74. nautobot/core/templates/base_react.html +55 -0
  75. nautobot/core/templates/buttons/export.html +6 -4
  76. nautobot/core/templates/generic/object_bulk_create.html +10 -21
  77. nautobot/core/templates/generic/object_list.html +3 -1
  78. nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
  79. nautobot/core/templates/inc/footer.html +1 -0
  80. nautobot/core/templates/inc/javascript.html +0 -14
  81. nautobot/core/templates/inc/nav_menu.html +28 -33
  82. nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
  83. nautobot/core/templates/inc/relationships_table_rows.html +2 -2
  84. nautobot/core/templates/nautobot_config.py.j2 +8 -20
  85. nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
  86. nautobot/core/templates/rest_framework/api.html +8 -0
  87. nautobot/core/templatetags/buttons.py +32 -28
  88. nautobot/core/testing/__init__.py +47 -44
  89. nautobot/core/testing/api.py +362 -47
  90. nautobot/core/testing/filters.py +1 -1
  91. nautobot/core/testing/migrations.py +2 -0
  92. nautobot/core/testing/mixins.py +22 -9
  93. nautobot/core/testing/schema.py +2 -1
  94. nautobot/core/testing/views.py +21 -46
  95. nautobot/core/tests/integration/test_filters.py +17 -8
  96. nautobot/core/tests/integration/test_navbar.py +11 -34
  97. nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
  98. nautobot/core/tests/nautobot_config.py +2 -3
  99. nautobot/core/tests/test_api.py +290 -21
  100. nautobot/core/tests/test_checks.py +0 -7
  101. nautobot/core/tests/test_filters.py +107 -59
  102. nautobot/core/tests/test_forms.py +26 -92
  103. nautobot/core/tests/test_graphql.py +110 -77
  104. nautobot/core/tests/test_logging.py +4 -0
  105. nautobot/core/tests/test_managers.py +3 -1
  106. nautobot/core/tests/test_models.py +2 -0
  107. nautobot/core/tests/test_paginator.py +3 -1
  108. nautobot/core/tests/test_releases.py +12 -12
  109. nautobot/core/tests/test_templatetags_helpers.py +4 -4
  110. nautobot/core/tests/test_utils.py +32 -68
  111. nautobot/core/tests/test_views.py +12 -15
  112. nautobot/core/utils/data.py +17 -0
  113. nautobot/core/utils/deprecation.py +9 -6
  114. nautobot/core/utils/filtering.py +8 -3
  115. nautobot/core/utils/git.py +12 -4
  116. nautobot/core/utils/lookup.py +3 -1
  117. nautobot/core/utils/requests.py +1 -104
  118. nautobot/core/views/__init__.py +1 -0
  119. nautobot/core/views/generic.py +75 -110
  120. nautobot/core/views/mixins.py +52 -61
  121. nautobot/core/views/renderers.py +6 -7
  122. nautobot/core/views/utils.py +80 -0
  123. nautobot/dcim/api/serializers.py +160 -667
  124. nautobot/dcim/api/urls.py +1 -1
  125. nautobot/dcim/api/views.py +7 -44
  126. nautobot/dcim/choices.py +2 -0
  127. nautobot/dcim/filters/__init__.py +21 -0
  128. nautobot/dcim/form_mixins.py +1 -27
  129. nautobot/dcim/forms.py +19 -765
  130. nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -1
  131. nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -13
  132. nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -1
  133. nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -1
  134. nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
  135. nautobot/dcim/migrations/0035_related_name_changes.py +1 -1
  136. nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -1
  137. nautobot/dcim/migrations/0040_tagsfield.py +109 -0
  138. nautobot/dcim/migrations/{0040_ipam__namespaces.py → 0041_ipam__namespaces.py} +1 -1
  139. nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
  140. nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
  141. nautobot/dcim/models/cables.py +3 -33
  142. nautobot/dcim/models/device_component_templates.py +6 -0
  143. nautobot/dcim/models/device_components.py +12 -198
  144. nautobot/dcim/models/devices.py +30 -143
  145. nautobot/dcim/models/locations.py +3 -64
  146. nautobot/dcim/models/power.py +3 -50
  147. nautobot/dcim/models/racks.py +7 -84
  148. nautobot/dcim/navigation.py +141 -467
  149. nautobot/dcim/signals.py +0 -2
  150. nautobot/dcim/tables/locations.py +2 -2
  151. nautobot/dcim/tables/power.py +1 -2
  152. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
  153. nautobot/dcim/templates/dcim/devicetype.html +2 -2
  154. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
  155. nautobot/dcim/templates/dcim/location.html +16 -1
  156. nautobot/dcim/templates/dcim/locationtype.html +15 -0
  157. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
  158. nautobot/dcim/templates/dcim/rackgroup.html +0 -12
  159. nautobot/dcim/tests/test_api.py +166 -81
  160. nautobot/dcim/tests/test_cablepaths.py +41 -35
  161. nautobot/dcim/tests/test_filters.py +67 -23
  162. nautobot/dcim/tests/test_forms.py +5 -205
  163. nautobot/dcim/tests/test_graphql.py +7 -2
  164. nautobot/dcim/tests/test_migrations.py +6 -11
  165. nautobot/dcim/tests/test_models.py +182 -110
  166. nautobot/dcim/tests/test_natural_ordering.py +11 -8
  167. nautobot/dcim/tests/test_signals.py +6 -3
  168. nautobot/dcim/tests/test_views.py +197 -175
  169. nautobot/dcim/urls.py +11 -16
  170. nautobot/dcim/views.py +7 -134
  171. nautobot/docs/additional-features/caching.md +6 -87
  172. nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
  173. nautobot/docs/additional-features/jobs.md +177 -195
  174. nautobot/docs/administration/nautobot-server.md +6 -21
  175. nautobot/docs/administration/replicating-nautobot.md +0 -10
  176. nautobot/docs/configuration/optional-settings.md +32 -41
  177. nautobot/docs/configuration/required-settings.md +11 -52
  178. nautobot/docs/development/application-registry.md +2 -13
  179. nautobot/docs/development/extending-models.md +15 -17
  180. nautobot/docs/development/generic-views.md +0 -2
  181. nautobot/docs/development/getting-started.md +55 -5
  182. nautobot/docs/development/navigation-menu.md +22 -93
  183. nautobot/docs/development/react-ui.md +105 -0
  184. nautobot/docs/development/role-internals.md +1 -3
  185. nautobot/docs/development/style-guide.md +6 -4
  186. nautobot/docs/index.md +3 -2
  187. nautobot/docs/installation/migrating-from-netbox.md +11 -42
  188. nautobot/docs/installation/nautobot.md +1 -1
  189. nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  190. nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  191. nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  192. nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  193. nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
  194. nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  195. nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  196. nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  197. nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  198. nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  199. nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  200. nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  201. nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  202. nautobot/docs/installation/upgrading-from-nautobot-v1.md +170 -747
  203. nautobot/docs/models/dcim/device.md +3 -0
  204. nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
  205. nautobot/docs/models/extras/computedfield.md +4 -4
  206. nautobot/docs/models/extras/gitrepository.md +3 -0
  207. nautobot/docs/models/extras/job.md +1 -0
  208. nautobot/docs/models/extras/jobbutton.md +18 -13
  209. nautobot/docs/models/extras/jobhook.md +7 -4
  210. nautobot/docs/models/extras/jobresult.md +6 -2
  211. nautobot/docs/models/extras/relationship.md +2 -2
  212. nautobot/docs/models/extras/status.md +6 -19
  213. nautobot/docs/models/ipam/ipaddress.md +3 -0
  214. nautobot/docs/models/virtualization/virtualmachine.md +3 -0
  215. nautobot/docs/plugins/development.md +83 -21
  216. nautobot/docs/release-notes/version-1.5.md +53 -0
  217. nautobot/docs/release-notes/version-2.0.md +180 -0
  218. nautobot/docs/requirements.txt +1 -0
  219. nautobot/docs/rest-api/overview.md +384 -215
  220. nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
  221. nautobot/extras/admin.py +3 -5
  222. nautobot/extras/api/customfields.py +15 -39
  223. nautobot/extras/api/fields.py +0 -11
  224. nautobot/extras/api/mixins.py +45 -0
  225. nautobot/extras/api/relationships.py +63 -158
  226. nautobot/extras/api/serializers.py +165 -700
  227. nautobot/extras/api/urls.py +1 -1
  228. nautobot/extras/api/views.py +294 -280
  229. nautobot/extras/apps.py +4 -7
  230. nautobot/extras/choices.py +11 -9
  231. nautobot/extras/constants.py +9 -3
  232. nautobot/extras/datasources/__init__.py +2 -0
  233. nautobot/extras/datasources/git.py +135 -186
  234. nautobot/extras/datasources/registry.py +25 -35
  235. nautobot/extras/filters/__init__.py +20 -19
  236. nautobot/extras/filters/mixins.py +4 -4
  237. nautobot/extras/forms/forms.py +63 -127
  238. nautobot/extras/forms/mixins.py +23 -51
  239. nautobot/extras/health_checks.py +0 -33
  240. nautobot/extras/jobs.py +387 -565
  241. nautobot/extras/management/commands/runjob.py +24 -62
  242. nautobot/extras/managers.py +30 -7
  243. nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
  244. nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -1
  245. nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -1
  246. nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -7
  247. nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -32
  248. nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -1
  249. nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -1
  250. nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
  251. nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -1
  252. nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -2
  253. nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -1
  254. nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -1
  255. nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
  256. nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -1
  257. nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
  258. nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -9
  259. nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -1
  260. nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
  261. nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
  262. nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
  263. nautobot/extras/migrations/{0077_remove_slug.py → 0078_remove_slug.py} +1 -5
  264. nautobot/extras/migrations/0079_tagsfield.py +28 -0
  265. nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
  266. nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
  267. nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
  268. nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
  269. nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
  270. nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
  271. nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
  272. nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
  273. nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
  274. nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
  275. nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
  276. nautobot/extras/models/__init__.py +2 -3
  277. nautobot/extras/models/change_logging.py +0 -36
  278. nautobot/extras/models/customfields.py +39 -33
  279. nautobot/extras/models/datasources.py +48 -50
  280. nautobot/extras/models/groups.py +5 -6
  281. nautobot/extras/models/jobs.py +189 -321
  282. nautobot/extras/models/mixins.py +0 -71
  283. nautobot/extras/models/models.py +0 -19
  284. nautobot/extras/models/relationships.py +19 -13
  285. nautobot/extras/models/roles.py +0 -34
  286. nautobot/extras/models/secrets.py +2 -26
  287. nautobot/extras/models/statuses.py +6 -5
  288. nautobot/extras/models/tags.py +2 -17
  289. nautobot/extras/navigation.py +89 -307
  290. nautobot/extras/plugins/__init__.py +3 -120
  291. nautobot/extras/plugins/utils.py +0 -3
  292. nautobot/extras/plugins/validators.py +5 -4
  293. nautobot/extras/plugins/views.py +16 -3
  294. nautobot/extras/querysets.py +1 -7
  295. nautobot/extras/registry.py +3 -0
  296. nautobot/extras/signals.py +26 -60
  297. nautobot/extras/tables.py +34 -40
  298. nautobot/extras/tasks.py +0 -12
  299. nautobot/extras/templates/extras/configcontext.html +1 -1
  300. nautobot/extras/templates/extras/configcontextschema.html +16 -1
  301. nautobot/extras/templates/extras/customfield.html +0 -13
  302. nautobot/extras/templates/extras/gitrepository.html +3 -3
  303. nautobot/extras/templates/extras/inc/jobresult.html +10 -0
  304. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  305. nautobot/extras/templates/extras/job.html +35 -25
  306. nautobot/extras/templates/extras/job_approval_request.html +15 -30
  307. nautobot/extras/templates/extras/job_detail.html +13 -31
  308. nautobot/extras/templates/extras/job_edit.html +15 -17
  309. nautobot/extras/templates/extras/jobresult.html +24 -6
  310. nautobot/extras/templates/extras/scheduledjob.html +2 -2
  311. nautobot/extras/templates/extras/secret.html +28 -0
  312. nautobot/extras/templatetags/job_buttons.py +1 -0
  313. nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
  314. nautobot/extras/test_jobs/atomic_transaction.py +53 -0
  315. nautobot/extras/test_jobs/dry_run.py +29 -0
  316. nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
  317. nautobot/extras/test_jobs/duplicate_name2.py +9 -0
  318. nautobot/extras/test_jobs/fail.py +23 -0
  319. nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
  320. nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
  321. nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
  322. nautobot/extras/test_jobs/file_upload_pass.py +25 -0
  323. nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
  324. nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
  325. nautobot/extras/test_jobs/job_button_receiver.py +28 -0
  326. nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
  327. nautobot/extras/test_jobs/job_variables.py +88 -0
  328. nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
  329. nautobot/extras/test_jobs/log_redaction.py +20 -0
  330. nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
  331. nautobot/extras/test_jobs/modify_db.py +25 -0
  332. nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
  333. nautobot/extras/test_jobs/object_var_optional.py +21 -0
  334. nautobot/extras/test_jobs/object_var_required.py +21 -0
  335. nautobot/extras/test_jobs/object_vars.py +26 -0
  336. nautobot/extras/test_jobs/pass.py +25 -0
  337. nautobot/extras/test_jobs/profiling.py +32 -0
  338. nautobot/extras/test_jobs/read_only_job.py +15 -0
  339. nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
  340. nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
  341. nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
  342. nautobot/extras/tests/integration/test_computedfields.py +1 -1
  343. nautobot/extras/tests/integration/test_configcontextschema.py +5 -3
  344. nautobot/extras/tests/integration/test_customfields.py +4 -2
  345. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  346. nautobot/extras/tests/integration/test_jobs.py +25 -27
  347. nautobot/extras/tests/integration/test_notes.py +8 -4
  348. nautobot/extras/tests/integration/test_relationships.py +2 -2
  349. nautobot/extras/tests/test_api.py +649 -642
  350. nautobot/extras/tests/test_changelog.py +3 -3
  351. nautobot/extras/tests/test_context_managers.py +5 -3
  352. nautobot/extras/tests/test_customfields.py +92 -50
  353. nautobot/extras/tests/test_datasources.py +189 -112
  354. nautobot/extras/tests/test_dynamicgroups.py +7 -8
  355. nautobot/extras/tests/test_filters.py +137 -89
  356. nautobot/extras/tests/test_forms.py +73 -75
  357. nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
  358. nautobot/extras/tests/test_jobs.py +262 -263
  359. nautobot/extras/tests/test_migrations.py +4 -3
  360. nautobot/extras/tests/test_models.py +116 -161
  361. nautobot/extras/tests/test_plugins.py +38 -60
  362. nautobot/extras/tests/test_relationships.py +167 -120
  363. nautobot/extras/tests/test_tags.py +6 -11
  364. nautobot/extras/tests/test_utils.py +31 -1
  365. nautobot/extras/tests/test_views.py +201 -145
  366. nautobot/extras/tests/test_webhooks.py +6 -2
  367. nautobot/extras/urls.py +42 -42
  368. nautobot/extras/utils.py +137 -163
  369. nautobot/extras/views.py +78 -152
  370. nautobot/ipam/api/fields.py +17 -0
  371. nautobot/ipam/api/serializers.py +58 -164
  372. nautobot/ipam/api/urls.py +1 -1
  373. nautobot/ipam/api/views.py +3 -2
  374. nautobot/ipam/apps.py +1 -2
  375. nautobot/ipam/filters.py +1 -10
  376. nautobot/ipam/forms.py +4 -177
  377. nautobot/ipam/lookups.py +1 -0
  378. nautobot/ipam/management/commands/__init__.py +0 -0
  379. nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
  380. nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -1
  381. nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -38
  382. nautobot/ipam/migrations/0020_related_name_changes.py +1 -1
  383. nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +2 -2
  384. nautobot/ipam/migrations/0028_tagsfield.py +44 -0
  385. nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
  386. nautobot/ipam/migrations/{0028_ipam__namespaces.py → 0030_ipam__namespaces.py} +77 -28
  387. nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
  388. nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
  389. nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
  390. nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
  391. nautobot/ipam/models.py +100 -236
  392. nautobot/ipam/navigation.py +36 -181
  393. nautobot/ipam/querysets.py +20 -25
  394. nautobot/ipam/signals.py +49 -6
  395. nautobot/ipam/tables.py +10 -3
  396. nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
  397. nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
  398. nautobot/ipam/templates/ipam/namespace_retrieve.html +17 -4
  399. nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
  400. nautobot/ipam/templates/ipam/prefix.html +1 -1
  401. nautobot/ipam/templates/ipam/vlangroup.html +0 -13
  402. nautobot/ipam/templates/ipam/vrf_edit.html +6 -0
  403. nautobot/ipam/tests/integration/test_prefixes.py +3 -26
  404. nautobot/ipam/tests/test_api.py +22 -19
  405. nautobot/ipam/tests/test_filters.py +59 -23
  406. nautobot/ipam/tests/test_migrations.py +6 -10
  407. nautobot/ipam/tests/test_models.py +323 -198
  408. nautobot/ipam/tests/test_ordering.py +2 -2
  409. nautobot/ipam/tests/test_querysets.py +44 -24
  410. nautobot/ipam/tests/test_views.py +73 -26
  411. nautobot/ipam/urls.py +16 -0
  412. nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
  413. nautobot/ipam/utils/migrations.py +713 -0
  414. nautobot/ipam/views.py +137 -20
  415. nautobot/project-static/docs/404.html +1178 -10
  416. nautobot/project-static/docs/additional-features/caching.html +1224 -159
  417. nautobot/project-static/docs/additional-features/change-logging.html +1180 -12
  418. nautobot/project-static/docs/additional-features/config-contexts.html +1180 -12
  419. nautobot/project-static/docs/additional-features/graphql.html +1179 -11
  420. nautobot/project-static/docs/additional-features/healthcheck.html +1180 -12
  421. nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1184 -12
  422. nautobot/project-static/docs/additional-features/jobs.html +1514 -328
  423. nautobot/project-static/docs/additional-features/napalm.html +1180 -12
  424. nautobot/project-static/docs/additional-features/prometheus-metrics.html +1180 -12
  425. nautobot/project-static/docs/additional-features/template-filters.html +1180 -12
  426. nautobot/project-static/docs/administration/celery-queues.html +1178 -10
  427. nautobot/project-static/docs/administration/nautobot-server.html +1451 -304
  428. nautobot/project-static/docs/administration/nautobot-shell.html +1178 -10
  429. nautobot/project-static/docs/administration/permissions.html +1178 -10
  430. nautobot/project-static/docs/administration/replicating-nautobot.html +1262 -113
  431. nautobot/project-static/docs/apps/index.html +1178 -10
  432. nautobot/project-static/docs/apps/nautobot-apps.html +1178 -10
  433. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1580 -426
  434. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1178 -10
  435. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3481 -1838
  436. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1178 -10
  437. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1178 -10
  438. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1185 -11
  439. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1719 -551
  440. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2062 -930
  441. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +1946 -659
  442. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1180 -12
  443. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1189 -21
  444. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9283 -6218
  445. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2734 -2122
  446. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1178 -10
  447. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2337 -1300
  448. nautobot/project-static/docs/configuration/authentication/ldap.html +1178 -10
  449. nautobot/project-static/docs/configuration/authentication/remote.html +1178 -10
  450. nautobot/project-static/docs/configuration/authentication/sso.html +1178 -10
  451. nautobot/project-static/docs/configuration/index.html +1178 -10
  452. nautobot/project-static/docs/configuration/optional-settings.html +1311 -160
  453. nautobot/project-static/docs/configuration/required-settings.html +1312 -211
  454. nautobot/project-static/docs/core-functionality/circuits.html +1178 -10
  455. nautobot/project-static/docs/core-functionality/device-types.html +1178 -10
  456. nautobot/project-static/docs/core-functionality/devices.html +1182 -10
  457. nautobot/project-static/docs/core-functionality/ipam.html +1182 -10
  458. nautobot/project-static/docs/core-functionality/power.html +1178 -10
  459. nautobot/project-static/docs/core-functionality/secrets.html +1178 -10
  460. nautobot/project-static/docs/core-functionality/services.html +1178 -10
  461. nautobot/project-static/docs/core-functionality/sites-and-racks.html +1178 -10
  462. nautobot/project-static/docs/core-functionality/tenancy.html +1178 -10
  463. nautobot/project-static/docs/core-functionality/virtualization.html +1182 -10
  464. nautobot/project-static/docs/core-functionality/vlans.html +1179 -11
  465. nautobot/project-static/docs/development/application-registry.html +1190 -42
  466. nautobot/project-static/docs/development/best-practices.html +1178 -10
  467. nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1178 -10
  468. nautobot/project-static/docs/development/extending-models.html +1238 -83
  469. nautobot/project-static/docs/development/generic-views.html +1180 -14
  470. nautobot/project-static/docs/development/getting-started.html +1365 -90
  471. nautobot/project-static/docs/development/homepage.html +1178 -10
  472. nautobot/project-static/docs/development/index.html +1178 -10
  473. nautobot/project-static/docs/development/model-features.html +1178 -10
  474. nautobot/project-static/docs/development/natural-keys.html +1178 -10
  475. nautobot/project-static/docs/development/navigation-menu.html +1215 -125
  476. nautobot/project-static/docs/development/react-ui.html +4199 -0
  477. nautobot/project-static/docs/development/release-checklist.html +1178 -10
  478. nautobot/project-static/docs/development/role-internals.html +1179 -12
  479. nautobot/project-static/docs/development/style-guide.html +1188 -19
  480. nautobot/project-static/docs/development/templates.html +1178 -10
  481. nautobot/project-static/docs/development/testing.html +1178 -10
  482. nautobot/project-static/docs/development/user-preferences.html +1178 -10
  483. nautobot/project-static/docs/docker/index.html +1178 -10
  484. nautobot/project-static/docs/index.html +1183 -12
  485. nautobot/project-static/docs/installation/centos.html +1178 -10
  486. nautobot/project-static/docs/installation/external-authentication.html +1178 -10
  487. nautobot/project-static/docs/installation/http-server.html +1178 -10
  488. nautobot/project-static/docs/installation/index.html +1178 -10
  489. nautobot/project-static/docs/installation/migrating-from-netbox.html +1305 -189
  490. nautobot/project-static/docs/installation/migrating-from-postgresql.html +1178 -10
  491. nautobot/project-static/docs/installation/nautobot.html +1179 -11
  492. nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1178 -10
  493. nautobot/project-static/docs/installation/selinux-troubleshooting.html +1178 -10
  494. nautobot/project-static/docs/installation/services.html +1178 -10
  495. nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  496. nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  497. nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  498. nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  499. nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
  500. nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  501. nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  502. nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  503. nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  504. nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  505. nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  506. nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  507. nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  508. nautobot/project-static/docs/installation/ubuntu.html +1178 -10
  509. nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +3823 -2152
  510. nautobot/project-static/docs/installation/upgrading.html +1178 -10
  511. nautobot/project-static/docs/models/circuits/circuit.html +1293 -103
  512. nautobot/project-static/docs/models/circuits/circuittermination.html +1293 -103
  513. nautobot/project-static/docs/models/circuits/circuittype.html +1293 -103
  514. nautobot/project-static/docs/models/circuits/provider.html +1293 -103
  515. nautobot/project-static/docs/models/circuits/providernetwork.html +1293 -103
  516. nautobot/project-static/docs/models/dcim/cable.html +1324 -103
  517. nautobot/project-static/docs/models/dcim/consoleport.html +1293 -103
  518. nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1293 -103
  519. nautobot/project-static/docs/models/dcim/consoleserverport.html +1293 -103
  520. nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1293 -103
  521. nautobot/project-static/docs/models/dcim/device.html +1326 -132
  522. nautobot/project-static/docs/models/dcim/devicebay.html +1293 -103
  523. nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1293 -103
  524. nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1379 -97
  525. nautobot/project-static/docs/models/dcim/devicetype.html +1293 -103
  526. nautobot/project-static/docs/models/dcim/frontport.html +1293 -103
  527. nautobot/project-static/docs/models/dcim/frontporttemplate.html +1293 -103
  528. nautobot/project-static/docs/models/dcim/interface.html +1293 -103
  529. nautobot/project-static/docs/models/dcim/interfacetemplate.html +1293 -103
  530. nautobot/project-static/docs/models/dcim/inventoryitem.html +1293 -103
  531. nautobot/project-static/docs/models/dcim/location.html +1293 -103
  532. nautobot/project-static/docs/models/dcim/locationtype.html +1293 -103
  533. nautobot/project-static/docs/models/dcim/manufacturer.html +1292 -102
  534. nautobot/project-static/docs/models/dcim/platform.html +1272 -82
  535. nautobot/project-static/docs/models/dcim/powerfeed.html +1270 -80
  536. nautobot/project-static/docs/models/dcim/poweroutlet.html +1272 -82
  537. nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1272 -82
  538. nautobot/project-static/docs/models/dcim/powerpanel.html +1270 -80
  539. nautobot/project-static/docs/models/dcim/powerport.html +1272 -82
  540. nautobot/project-static/docs/models/dcim/powerporttemplate.html +1272 -82
  541. nautobot/project-static/docs/models/dcim/rack.html +1272 -82
  542. nautobot/project-static/docs/models/dcim/rackgroup.html +1272 -82
  543. nautobot/project-static/docs/models/dcim/rackreservation.html +1272 -82
  544. nautobot/project-static/docs/models/dcim/rearport.html +1286 -96
  545. nautobot/project-static/docs/models/dcim/rearporttemplate.html +1286 -96
  546. nautobot/project-static/docs/models/dcim/region.html +1178 -10
  547. nautobot/project-static/docs/models/dcim/site.html +1178 -10
  548. nautobot/project-static/docs/models/dcim/virtualchassis.html +1284 -94
  549. nautobot/project-static/docs/models/extras/computedfield.html +1184 -16
  550. nautobot/project-static/docs/models/extras/configcontext.html +1314 -86
  551. nautobot/project-static/docs/models/extras/configcontextschema.html +1276 -86
  552. nautobot/project-static/docs/models/extras/customfield.html +1180 -12
  553. nautobot/project-static/docs/models/extras/customlink.html +1180 -12
  554. nautobot/project-static/docs/models/extras/dynamicgroup.html +1180 -12
  555. nautobot/project-static/docs/models/extras/exporttemplate.html +1180 -12
  556. nautobot/project-static/docs/models/extras/gitrepository.html +1184 -12
  557. nautobot/project-static/docs/models/extras/graphqlquery.html +1321 -86
  558. nautobot/project-static/docs/models/extras/imageattachment.html +1276 -86
  559. nautobot/project-static/docs/models/extras/job.html +1277 -86
  560. nautobot/project-static/docs/models/extras/jobbutton.html +1201 -29
  561. nautobot/project-static/docs/models/extras/jobhook.html +1188 -16
  562. nautobot/project-static/docs/models/extras/joblogentry.html +1274 -84
  563. nautobot/project-static/docs/models/extras/jobresult.html +1364 -169
  564. nautobot/project-static/docs/models/extras/note.html +1180 -12
  565. nautobot/project-static/docs/models/extras/relationship.html +1182 -14
  566. nautobot/project-static/docs/models/extras/role.html +1320 -86
  567. nautobot/project-static/docs/models/extras/secret.html +1314 -86
  568. nautobot/project-static/docs/models/extras/secretsgroup.html +1276 -86
  569. nautobot/project-static/docs/models/extras/status.html +1188 -59
  570. nautobot/project-static/docs/models/extras/tag.html +1180 -12
  571. nautobot/project-static/docs/models/extras/webhook.html +1180 -12
  572. nautobot/project-static/docs/models/ipam/ipaddress.html +1327 -102
  573. nautobot/project-static/docs/models/ipam/prefix.html +1276 -86
  574. nautobot/project-static/docs/models/ipam/rir.html +1276 -86
  575. nautobot/project-static/docs/models/ipam/routetarget.html +1276 -86
  576. nautobot/project-static/docs/models/ipam/service.html +1276 -86
  577. nautobot/project-static/docs/models/ipam/vlan.html +1276 -86
  578. nautobot/project-static/docs/models/ipam/vlangroup.html +1276 -86
  579. nautobot/project-static/docs/models/ipam/vrf.html +1276 -86
  580. nautobot/project-static/docs/models/tenancy/tenant.html +1276 -86
  581. nautobot/project-static/docs/models/tenancy/tenantgroup.html +1276 -86
  582. nautobot/project-static/docs/models/users/objectpermission.html +1314 -86
  583. nautobot/project-static/docs/models/users/token.html +1276 -86
  584. nautobot/project-static/docs/models/virtualization/cluster.html +1276 -86
  585. nautobot/project-static/docs/models/virtualization/clustergroup.html +1276 -86
  586. nautobot/project-static/docs/models/virtualization/clustertype.html +1276 -86
  587. nautobot/project-static/docs/models/virtualization/virtualmachine.html +1321 -127
  588. nautobot/project-static/docs/models/virtualization/vminterface.html +1276 -86
  589. nautobot/project-static/docs/objects.inv +0 -0
  590. nautobot/project-static/docs/plugins/development.html +1726 -495
  591. nautobot/project-static/docs/plugins/index.html +1178 -10
  592. nautobot/project-static/docs/plugins/porting-from-netbox.html +1178 -10
  593. nautobot/project-static/docs/release-notes/index.html +1178 -10
  594. nautobot/project-static/docs/release-notes/version-1.0.html +1178 -10
  595. nautobot/project-static/docs/release-notes/version-1.1.html +1178 -10
  596. nautobot/project-static/docs/release-notes/version-1.2.html +1178 -10
  597. nautobot/project-static/docs/release-notes/version-1.3.html +1178 -10
  598. nautobot/project-static/docs/release-notes/version-1.4.html +1178 -10
  599. nautobot/project-static/docs/release-notes/version-1.5.html +1608 -225
  600. nautobot/project-static/docs/release-notes/version-2.0.html +1547 -47
  601. nautobot/project-static/docs/requirements.txt +1 -0
  602. nautobot/project-static/docs/rest-api/authentication.html +1179 -11
  603. nautobot/project-static/docs/rest-api/filtering.html +1178 -10
  604. nautobot/project-static/docs/rest-api/overview.html +1841 -446
  605. nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
  606. nautobot/project-static/docs/search/search_index.json +1 -1
  607. nautobot/project-static/docs/sitemap.xml +197 -187
  608. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  609. nautobot/project-static/docs/user-guides/custom-fields.html +1178 -10
  610. nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1178 -10
  611. nautobot/project-static/docs/user-guides/getting-started/index.html +1178 -10
  612. nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1178 -10
  613. nautobot/project-static/docs/user-guides/getting-started/ipam.html +1178 -10
  614. nautobot/project-static/docs/user-guides/getting-started/platforms.html +1178 -10
  615. nautobot/project-static/docs/user-guides/getting-started/regions.html +1178 -10
  616. nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1178 -10
  617. nautobot/project-static/docs/user-guides/getting-started/tenants.html +1178 -10
  618. nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1178 -10
  619. nautobot/project-static/docs/user-guides/git-data-source.html +1178 -10
  620. nautobot/project-static/docs/user-guides/graphql.html +1178 -10
  621. nautobot/project-static/docs/user-guides/relationships.html +1178 -10
  622. nautobot/project-static/docs/user-guides/s3-django-storage.html +1178 -10
  623. nautobot/project-static/js/forms.js +16 -9
  624. nautobot/project-static/js/theme.js +5 -0
  625. nautobot/tenancy/api/serializers.py +4 -32
  626. nautobot/tenancy/api/urls.py +1 -1
  627. nautobot/tenancy/forms.py +0 -28
  628. nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
  629. nautobot/tenancy/models.py +0 -25
  630. nautobot/tenancy/navigation.py +6 -39
  631. nautobot/tenancy/templates/tenancy/tenant.html +12 -12
  632. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  633. nautobot/tenancy/tests/test_api.py +1 -3
  634. nautobot/tenancy/tests/test_filters.py +10 -5
  635. nautobot/tenancy/views.py +0 -2
  636. nautobot/ui/.eslintignore +6 -0
  637. nautobot/ui/.gitignore +10 -0
  638. nautobot/ui/.prettierignore +9 -0
  639. nautobot/ui/.prettierrc +4 -0
  640. nautobot/ui/README.md +33 -0
  641. nautobot/ui/app_imports.js.j2 +7 -0
  642. nautobot/ui/craco.config.js +46 -0
  643. nautobot/ui/jsconfig-base.json +11 -0
  644. nautobot/ui/jsconfig.json +5 -0
  645. nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
  646. nautobot/ui/package-lock.json +21451 -0
  647. nautobot/ui/package.json +70 -0
  648. nautobot/ui/public/index.html +47 -0
  649. nautobot/ui/public/logo192.png +0 -0
  650. nautobot/ui/public/logo512.png +0 -0
  651. nautobot/ui/public/manifest.json +25 -0
  652. nautobot/ui/public/nautobot_logo.svg +131 -0
  653. nautobot/ui/public/robots.txt +3 -0
  654. nautobot/ui/src/App.js +71 -0
  655. nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
  656. nautobot/ui/src/components/AppTab.js +40 -0
  657. nautobot/ui/src/components/Apps.js +60 -0
  658. nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
  659. nautobot/ui/src/components/HomePanel.js +58 -0
  660. nautobot/ui/src/components/JobHistoryTable.js +78 -0
  661. nautobot/ui/src/components/Layout.js +53 -0
  662. nautobot/ui/src/components/LoadingWidget.js +25 -0
  663. nautobot/ui/src/components/Navbar.js +116 -0
  664. nautobot/ui/src/components/NotificationPopover.js +27 -0
  665. nautobot/ui/src/components/ObjectListTable.js +209 -0
  666. nautobot/ui/src/components/ReferenceDataTag.js +35 -0
  667. nautobot/ui/src/components/RouterButton.js +10 -0
  668. nautobot/ui/src/components/RouterLink.js +10 -0
  669. nautobot/ui/src/components/SidebarNav.js +147 -0
  670. nautobot/ui/src/components/Table.js +48 -0
  671. nautobot/ui/src/components/TableItem.js +71 -0
  672. nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
  673. nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
  674. nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
  675. nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
  676. nautobot/ui/src/components/__tests__/Table.test.js +36 -0
  677. nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
  678. nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
  679. nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
  680. nautobot/ui/src/components/pagination.js +93 -0
  681. nautobot/ui/src/components/paginator.js +79 -0
  682. nautobot/ui/src/components/paginator_form.js +43 -0
  683. nautobot/ui/src/components/usePagination.js +57 -0
  684. nautobot/ui/src/constants/apiPath.js +10 -0
  685. nautobot/ui/src/constants/icons.js +15 -0
  686. nautobot/ui/src/constants/size.js +15 -0
  687. nautobot/ui/src/index.js +65 -0
  688. nautobot/ui/src/reportWebVitals.js +15 -0
  689. nautobot/ui/src/router.js +77 -0
  690. nautobot/ui/src/utils/api.js +131 -0
  691. nautobot/ui/src/utils/app-import.js +15 -0
  692. nautobot/ui/src/utils/color.js +15 -0
  693. nautobot/ui/src/utils/date.js +14 -0
  694. nautobot/ui/src/utils/index.js +15 -0
  695. nautobot/ui/src/utils/navigation.js +32 -0
  696. nautobot/ui/src/utils/session.js +64 -0
  697. nautobot/ui/src/utils/store.js +242 -0
  698. nautobot/ui/src/utils/string.js +6 -0
  699. nautobot/ui/src/utils/url.js +4 -0
  700. nautobot/ui/src/views/Home.js +138 -0
  701. nautobot/ui/src/views/InstalledApps.js +80 -0
  702. nautobot/ui/src/views/Login.js +48 -0
  703. nautobot/ui/src/views/Logout.js +20 -0
  704. nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
  705. nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
  706. nautobot/ui/src/views/__tests__/Login.test.js +15 -0
  707. nautobot/ui/src/views/generic/GenericView.js +142 -0
  708. nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
  709. nautobot/ui/src/views/generic/ObjectList.js +127 -0
  710. nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
  711. nautobot/users/admin.py +1 -1
  712. nautobot/users/api/serializers.py +51 -61
  713. nautobot/users/api/urls.py +1 -1
  714. nautobot/users/api/views.py +53 -2
  715. nautobot/users/tests/test_api.py +110 -25
  716. nautobot/virtualization/api/serializers.py +18 -130
  717. nautobot/virtualization/api/urls.py +1 -1
  718. nautobot/virtualization/api/views.py +1 -22
  719. nautobot/virtualization/forms.py +13 -99
  720. nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -1
  721. nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -11
  722. nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -1
  723. nautobot/virtualization/migrations/0018_related_name_changes.py +1 -1
  724. nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
  725. nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
  726. nautobot/virtualization/migrations/{0021_ipam__namespaces.py → 0023_ipam__namespaces.py} +2 -2
  727. nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
  728. nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
  729. nautobot/virtualization/models.py +31 -123
  730. nautobot/virtualization/navigation.py +18 -99
  731. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -1
  732. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
  733. nautobot/virtualization/tests/test_api.py +25 -26
  734. nautobot/virtualization/tests/test_filters.py +41 -15
  735. nautobot/virtualization/tests/test_models.py +31 -7
  736. nautobot/virtualization/tests/test_views.py +42 -25
  737. nautobot/virtualization/views.py +7 -6
  738. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +3 -7
  739. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +744 -602
  740. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
  741. nautobot/circuits/api/nested_serializers.py +0 -69
  742. nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
  743. nautobot/dcim/api/nested_serializers.py +0 -356
  744. nautobot/dcim/templates/dcim/device_import.html +0 -5
  745. nautobot/dcim/templates/dcim/device_import_child.html +0 -5
  746. nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
  747. nautobot/extras/api/nested_serializers.py +0 -353
  748. nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -41
  749. nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -46
  750. nautobot/extras/reports.py +0 -60
  751. nautobot/extras/scripts.py +0 -72
  752. nautobot/extras/tests/example_jobs/script_variables.py +0 -67
  753. nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
  754. nautobot/extras/tests/example_jobs/test_fail.py +0 -16
  755. nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
  756. nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
  757. nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
  758. nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
  759. nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
  760. nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
  761. nautobot/extras/tests/example_jobs/test_modify_db.py +0 -18
  762. nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
  763. nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
  764. nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
  765. nautobot/extras/tests/example_jobs/test_pass.py +0 -19
  766. nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
  767. nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
  768. nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
  769. nautobot/ipam/api/nested_serializers.py +0 -159
  770. nautobot/ipam/migrations/0029_ipam__prefix__add_parent.py +0 -31
  771. nautobot/ipam/migrations/0030_ipam__prefix__data_migration.py +0 -13
  772. nautobot/ipam/migrations/0031_ipam__ipaddress__add_parent.py +0 -41
  773. nautobot/ipam/migrations/0032_ipam__ipaddress__data_migration.py +0 -11
  774. nautobot/tenancy/api/nested_serializers.py +0 -31
  775. nautobot/users/api/nested_serializers.py +0 -67
  776. nautobot/virtualization/api/nested_serializers.py +0 -65
  777. /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
  778. /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
  779. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
  780. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,4 @@
1
1
  import uuid
2
- from unittest import skip
3
2
 
4
3
  from django.contrib.auth import get_user_model
5
4
  from django.contrib.contenttypes.models import ContentType
@@ -88,7 +87,6 @@ from nautobot.virtualization.models import Cluster, ClusterGroup, ClusterType, V
88
87
  User = get_user_model()
89
88
 
90
89
 
91
- @skip(reason="Content Types are BROKEN")
92
90
  class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
93
91
  queryset = ComputedField.objects.all()
94
92
  filterset = ComputedFieldFilterSet
@@ -97,7 +95,7 @@ class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
97
95
  def setUpTestData(cls):
98
96
  ComputedField.objects.create(
99
97
  content_type=ContentType.objects.get_for_model(Location),
100
- slug="computed_field_one",
98
+ key="computed_field_one",
101
99
  label="Computed Field One",
102
100
  template="{{ obj.name }} is the name of this location.",
103
101
  fallback_value="An error occurred while rendering this template.",
@@ -106,7 +104,7 @@ class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
106
104
  # Field whose template will raise a TemplateError
107
105
  ComputedField.objects.create(
108
106
  content_type=ContentType.objects.get_for_model(Location),
109
- slug="bad_computed_field",
107
+ key="bad_computed_field",
110
108
  label="Bad Computed Field",
111
109
  template="{{ something_that_throws_an_err | not_a_real_filter }} bad data",
112
110
  fallback_value="This template has errored",
@@ -115,7 +113,7 @@ class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
115
113
  # Field whose template will raise a TypeError
116
114
  ComputedField.objects.create(
117
115
  content_type=ContentType.objects.get_for_model(Location),
118
- slug="worse_computed_field",
116
+ key="worse_computed_field",
119
117
  label="Worse Computed Field",
120
118
  template="{{ obj.images | list }}",
121
119
  fallback_value="Another template error",
@@ -123,15 +121,15 @@ class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
123
121
  )
124
122
  ComputedField.objects.create(
125
123
  content_type=ContentType.objects.get_for_model(Device),
126
- slug="device_computed_field",
124
+ key="device_computed_field",
127
125
  label="Device Computed Field",
128
126
  template="Hello, world.",
129
127
  fallback_value="This template has errored",
130
128
  weight=100,
131
129
  )
132
130
 
133
- def test_slug(self):
134
- params = {"slug": ["device_computed_field", "worse_computed_field"]}
131
+ def test_key(self):
132
+ params = {"key": ["device_computed_field", "worse_computed_field"]}
135
133
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
136
134
 
137
135
  def test_content_type(self):
@@ -548,7 +546,7 @@ class GitRepositoryTestCase(FilterTestCases.FilterTestCase):
548
546
  ),
549
547
  )
550
548
  for repo in repos:
551
- repo.save(trigger_resync=False)
549
+ repo.save()
552
550
  repos[0].tags.set(Tag.objects.get_for_model(GitRepository))
553
551
  repos[1].tags.set(Tag.objects.get_for_model(GitRepository)[:3])
554
552
 
@@ -715,9 +713,10 @@ class ImageAttachmentTestCase(FilterTestCases.FilterTestCase):
715
713
 
716
714
  locations = Location.objects.filter(location_type=LocationType.objects.get(name="Campus"))[:2]
717
715
 
716
+ rack_status = Status.objects.get_for_model(Rack).first()
718
717
  racks = (
719
- Rack.objects.create(name="Rack 1", location=locations[0]),
720
- Rack.objects.create(name="Rack 2", location=locations[1]),
718
+ Rack.objects.create(name="Rack 1", location=locations[0], status=rack_status),
719
+ Rack.objects.create(name="Rack 2", location=locations[1], status=rack_status),
721
720
  )
722
721
 
723
722
  ImageAttachment.objects.create(
@@ -769,7 +768,7 @@ class ImageAttachmentTestCase(FilterTestCases.FilterTestCase):
769
768
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
770
769
 
771
770
 
772
- class JobFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
771
+ class JobFilterSetTestCase(FilterTestCases.NameOnlyFilterTestCase):
773
772
  queryset = Job.objects.all()
774
773
  filterset = JobFilterSet
775
774
 
@@ -779,7 +778,7 @@ class JobFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
779
778
  Job.objects.last().tags.set(Tag.objects.get_for_model(Job)[:3])
780
779
 
781
780
  def test_grouping(self):
782
- params = {"grouping": ["test_file_upload_pass", "test_file_upload_fail"]}
781
+ params = {"grouping": ["file_upload_pass", "file_upload_fail"]}
783
782
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
784
783
 
785
784
  def test_installed(self):
@@ -790,8 +789,8 @@ class JobFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
790
789
  params = {"job_class_name": "TestPass", "enabled": False}
791
790
  self.assertTrue(self.filterset(params, self.queryset).qs.exists())
792
791
 
793
- def test_commit_default(self):
794
- params = {"commit_default": False}
792
+ def test_dryrun_default(self):
793
+ params = {"dryrun_default": True}
795
794
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
796
795
 
797
796
  def test_hidden(self):
@@ -800,15 +799,27 @@ class JobFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
800
799
 
801
800
  def test_read_only(self):
802
801
  params = {"read_only": True}
803
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
802
+ self.assertQuerysetEqualAndNotEmpty(
803
+ self.filterset(params, self.queryset).qs, self.queryset.filter(read_only=True)
804
+ )
804
805
 
805
806
  def test_approval_required(self):
806
807
  params = {"approval_required": True}
807
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
808
+ self.assertQuerysetEqualAndNotEmpty(
809
+ self.filterset(params, self.queryset).qs,
810
+ self.queryset.filter(approval_required=True),
811
+ )
808
812
 
809
813
  def test_search(self):
810
814
  params = {"q": "file"}
811
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
815
+ expected_matches = (
816
+ Q(name__icontains="file") # pylint: disable=unsupported-binary-operation
817
+ | Q(grouping__icontains="file")
818
+ | Q(description__icontains="file")
819
+ )
820
+ self.assertQuerysetEqualAndNotEmpty(
821
+ self.filterset(params, self.queryset).qs, self.queryset.filter(expected_matches)
822
+ )
812
823
  value = self.queryset.values_list("pk", flat=True)[0]
813
824
  params = {"q": value}
814
825
  self.assertEqual(self.filterset(params, self.queryset).qs.values_list("pk", flat=True)[0], value)
@@ -831,16 +842,14 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
831
842
  job_model=job,
832
843
  name=job.class_path,
833
844
  user=User.objects.first(),
834
- obj_type=ContentType.objects.get_for_model(Job),
835
845
  status=JobResultStatusChoices.STATUS_STARTED,
836
- task_id=job.pk,
837
846
  )
838
847
 
839
848
  def test_job_model(self):
840
849
  jobs = list(self.jobs[:2])
841
850
  filter_params = [
842
851
  {"job_model_id": [jobs[0].pk, jobs[1].pk]},
843
- {"job_model": [jobs[0].pk, jobs[1].slug]},
852
+ {"job_model": [jobs[0].pk, jobs[1].name]},
844
853
  ]
845
854
  for params in filter_params:
846
855
  self.assertQuerysetEqualAndNotEmpty(
@@ -895,7 +904,7 @@ class JobHookFilterSetTestCase(FilterTestCases.NameOnlyFilterTestCase):
895
904
 
896
905
  def test_job(self):
897
906
  jobs = Job.objects.filter(job_class_name__in=["TestJobHookReceiverLog", "TestJobHookReceiverChange"])[:2]
898
- params = {"job": [jobs[0].slug, jobs[1].pk]}
907
+ params = {"job": [jobs[0].name, jobs[1].pk]}
899
908
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
900
909
 
901
910
  def test_type_create(self):
@@ -964,9 +973,9 @@ class JobButtonFilterTestCase(FilterTestCases.FilterTestCase):
964
973
  self.filterset(params, self.queryset).qs, self.queryset.filter(job__pk=job.pk)
965
974
  )
966
975
 
967
- params = {"job": [job.slug]}
976
+ params = {"job": [job.name]}
968
977
  self.assertQuerysetEqualAndNotEmpty(
969
- self.filterset(params, self.queryset).qs, self.queryset.filter(job__slug=job.slug)
978
+ self.filterset(params, self.queryset).qs, self.queryset.filter(job__name=job.name)
970
979
  )
971
980
 
972
981
  def test_weight(self):
@@ -989,13 +998,9 @@ class JobLogEntryTestCase(FilterTestCases.FilterTestCase):
989
998
 
990
999
  @classmethod
991
1000
  def setUpTestData(cls):
992
- cls.job_result = JobResult.objects.create(
993
- name="test",
994
- task_id=uuid.uuid4(),
995
- obj_type=ContentType.objects.get_for_model(GitRepository),
996
- )
1001
+ cls.job_result = JobResult.objects.create(name="test")
997
1002
 
998
- for log_level in ("debug", "info", "success", "warning"):
1003
+ for log_level in ("debug", "info", "warning", "error", "critical"):
999
1004
  JobLogEntry.objects.create(
1000
1005
  log_level=log_level,
1001
1006
  grouping="run",
@@ -1004,24 +1009,36 @@ class JobLogEntryTestCase(FilterTestCases.FilterTestCase):
1004
1009
  )
1005
1010
 
1006
1011
  def test_log_level(self):
1007
- params = {"log_level": ["success"]}
1008
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1012
+ params = {"log_level": ["debug"]}
1013
+ self.assertQuerysetEqualAndNotEmpty(
1014
+ self.filterset(params, self.queryset).qs, self.queryset.filter(log_level="debug")
1015
+ )
1009
1016
 
1010
1017
  def test_grouping(self):
1011
1018
  params = {"grouping": ["run"]}
1012
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
1019
+ self.assertQuerysetEqualAndNotEmpty(
1020
+ self.filterset(params, self.queryset).qs, self.queryset.filter(grouping="run")
1021
+ )
1013
1022
 
1014
1023
  def test_message(self):
1015
- params = {"message": ["I am a success log."]}
1016
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1024
+ params = {"message": ["I am a debug log."]}
1025
+ self.assertQuerysetEqualAndNotEmpty(
1026
+ self.filterset(params, self.queryset).qs, self.queryset.filter(message="I am a debug log.")
1027
+ )
1017
1028
 
1018
1029
  def test_search(self):
1019
1030
  params = {"q": "run"}
1020
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
1031
+ self.assertQuerysetEqualAndNotEmpty(
1032
+ self.filterset(params, self.queryset).qs, self.queryset.filter(grouping__icontains="run")
1033
+ )
1021
1034
  params = {"q": "warning log"}
1022
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1023
- params = {"q": "success"}
1024
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1035
+ self.assertQuerysetEqualAndNotEmpty(
1036
+ self.filterset(params, self.queryset).qs, self.queryset.filter(message__icontains="warning log")
1037
+ )
1038
+ params = {"q": "debug"}
1039
+ self.assertQuerysetEqualAndNotEmpty(
1040
+ self.filterset(params, self.queryset).qs, self.queryset.filter(log_level__icontains="debug")
1041
+ )
1025
1042
 
1026
1043
 
1027
1044
  class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
@@ -1037,9 +1054,11 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1037
1054
  )
1038
1055
 
1039
1056
  location = Location.objects.first()
1057
+ ipaddr_status = Status.objects.get_for_model(IPAddress).first()
1058
+ prefix_status = Status.objects.get_for_model(Prefix).first()
1040
1059
  namespace = Namespace.objects.first()
1041
- Prefix.objects.create(prefix="192.0.2.0/24", namespace=namespace)
1042
- ipaddress = IPAddress.objects.create(address="192.0.2.1/24", namespace=namespace)
1060
+ Prefix.objects.create(prefix="192.0.2.0/24", namespace=namespace, status=prefix_status)
1061
+ ipaddress = IPAddress.objects.create(address="192.0.2.1/24", namespace=namespace, status=ipaddr_status)
1043
1062
 
1044
1063
  ObjectChange.objects.create(
1045
1064
  user=users[0],
@@ -1075,7 +1094,7 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1075
1094
  action=ObjectChangeActionChoices.ACTION_CREATE,
1076
1095
  changed_object=ipaddress,
1077
1096
  object_repr=str(ipaddress),
1078
- object_data={"address": str(ipaddress.address), "status": ipaddress.status},
1097
+ object_data={"address": str(ipaddress.address), "status": str(ipaddress.status)},
1079
1098
  )
1080
1099
  ObjectChange.objects.create(
1081
1100
  user=users[2],
@@ -1084,7 +1103,7 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1084
1103
  action=ObjectChangeActionChoices.ACTION_UPDATE,
1085
1104
  changed_object=ipaddress,
1086
1105
  object_repr=str(ipaddress),
1087
- object_data={"address": str(ipaddress.address), "status": ipaddress.status},
1106
+ object_data={"address": str(ipaddress.address), "status": str(ipaddress.status)},
1088
1107
  )
1089
1108
  ObjectChange.objects.create(
1090
1109
  user=users[2],
@@ -1093,7 +1112,7 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1093
1112
  action=ObjectChangeActionChoices.ACTION_DELETE,
1094
1113
  changed_object=ipaddress,
1095
1114
  object_repr=str(ipaddress),
1096
- object_data={"address": str(ipaddress.address), "status": ipaddress.status},
1115
+ object_data={"address": str(ipaddress.address), "status": str(ipaddress.status)},
1097
1116
  )
1098
1117
 
1099
1118
  def test_user(self):
@@ -1125,8 +1144,7 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1125
1144
  self.assertEqual(self.filterset(params, self.queryset).qs.values_list("pk", flat=True)[0], value)
1126
1145
 
1127
1146
 
1128
- @skip(reason="Content Types are BROKEN")
1129
- class RelationshipTestCase(FilterTestCases.NameSlugFilterTestCase):
1147
+ class RelationshipTestCase(FilterTestCases.FilterTestCase):
1130
1148
  queryset = Relationship.objects.all()
1131
1149
  filterset = RelationshipFilterSet
1132
1150
 
@@ -1137,27 +1155,43 @@ class RelationshipTestCase(FilterTestCases.NameSlugFilterTestCase):
1137
1155
  vlan_type = ContentType.objects.get_for_model(VLAN)
1138
1156
 
1139
1157
  Relationship(
1140
- name="Device VLANs",
1141
- slug="device-vlans",
1158
+ label="Device VLANs",
1159
+ key="device_vlans",
1142
1160
  type="many-to-many",
1143
1161
  source_type=device_type,
1144
1162
  destination_type=vlan_type,
1145
1163
  ).validated_save()
1146
1164
  Relationship(
1147
- name="Primary VLAN",
1148
- slug="primary-vlan",
1165
+ label="Primary VLAN",
1166
+ key="primary_vlan",
1149
1167
  type="one-to-many",
1150
1168
  source_type=vlan_type,
1151
1169
  destination_type=device_type,
1152
1170
  ).validated_save()
1153
1171
  Relationship(
1154
- name="Primary Interface",
1155
- slug="primary-interface",
1172
+ label="Primary Interface",
1173
+ key="primary_interface",
1156
1174
  type="one-to-one",
1157
1175
  source_type=device_type,
1158
1176
  destination_type=interface_type,
1159
1177
  ).validated_save()
1160
1178
 
1179
+ def test_label(self):
1180
+ """Verify that the filterset supports filtering by label."""
1181
+ params = {"label": list(self.queryset.values_list("label", flat=True)[:2])}
1182
+ filterset = self.filterset(params, self.queryset)
1183
+ self.assertTrue(filterset.is_valid())
1184
+ self.assertQuerysetEqualAndNotEmpty(
1185
+ filterset.qs.order_by("label"), self.queryset.filter(label__in=params["label"]).order_by("label")
1186
+ )
1187
+
1188
+ def test_key(self):
1189
+ """Verify that the filterset supports filtering by key."""
1190
+ params = {"key": self.queryset.values_list("key", flat=True)[:2]}
1191
+ filterset = self.filterset(params, self.queryset)
1192
+ self.assertTrue(filterset.is_valid())
1193
+ self.assertEqual(filterset.qs.count(), 2)
1194
+
1161
1195
  def test_type(self):
1162
1196
  params = {"type": ["one-to-many"]}
1163
1197
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@@ -1171,7 +1205,6 @@ class RelationshipTestCase(FilterTestCases.NameSlugFilterTestCase):
1171
1205
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
1172
1206
 
1173
1207
 
1174
- @skip(reason="Content Types are BROKEN")
1175
1208
  class RelationshipAssociationTestCase(FilterTestCases.FilterTestCase):
1176
1209
  queryset = RelationshipAssociation.objects.all()
1177
1210
  filterset = RelationshipAssociationFilterSet
@@ -1183,22 +1216,22 @@ class RelationshipAssociationTestCase(FilterTestCases.FilterTestCase):
1183
1216
 
1184
1217
  cls.relationships = (
1185
1218
  Relationship(
1186
- name="Device VLANs",
1187
- slug="device-vlans",
1219
+ label="Device VLANs",
1220
+ key="device_vlans",
1188
1221
  type="many-to-many",
1189
1222
  source_type=cls.device_type,
1190
1223
  destination_type=cls.vlan_type,
1191
1224
  ),
1192
1225
  Relationship(
1193
- name="Primary VLAN",
1194
- slug="primary-vlan",
1226
+ label="Primary VLAN",
1227
+ key="primary_vlan",
1195
1228
  type="one-to-many",
1196
1229
  source_type=cls.vlan_type,
1197
1230
  destination_type=cls.device_type,
1198
1231
  ),
1199
1232
  Relationship(
1200
- name="Device Device",
1201
- slug="symmetric-device-device",
1233
+ label="Device Device",
1234
+ key="symmetric_device_device",
1202
1235
  type="symmetric-many-to-many",
1203
1236
  source_type=cls.device_type,
1204
1237
  destination_type=cls.device_type,
@@ -1210,15 +1243,23 @@ class RelationshipAssociationTestCase(FilterTestCases.FilterTestCase):
1210
1243
  manufacturer = Manufacturer.objects.first()
1211
1244
  devicetype = DeviceType.objects.create(manufacturer=manufacturer, model="Device Type 1", slug="device-type-1")
1212
1245
  devicerole = Role.objects.get_for_model(Device).first()
1246
+ devicestatus = Status.objects.get_for_model(Device).first()
1213
1247
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1214
1248
  cls.devices = (
1215
- Device.objects.create(name="Device 1", device_type=devicetype, role=devicerole, location=location),
1216
- Device.objects.create(name="Device 2", device_type=devicetype, role=devicerole, location=location),
1217
- Device.objects.create(name="Device 3", device_type=devicetype, role=devicerole, location=location),
1249
+ Device.objects.create(
1250
+ name="Device 1", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1251
+ ),
1252
+ Device.objects.create(
1253
+ name="Device 2", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1254
+ ),
1255
+ Device.objects.create(
1256
+ name="Device 3", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1257
+ ),
1218
1258
  )
1259
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1219
1260
  cls.vlans = (
1220
- VLAN.objects.create(vid=1, name="VLAN 1"),
1221
- VLAN.objects.create(vid=2, name="VLAN 2"),
1261
+ VLAN.objects.create(vid=1, name="VLAN 1", status=vlan_status),
1262
+ VLAN.objects.create(vid=2, name="VLAN 2", status=vlan_status),
1222
1263
  )
1223
1264
 
1224
1265
  RelationshipAssociation(
@@ -1265,7 +1306,7 @@ class RelationshipAssociationTestCase(FilterTestCases.FilterTestCase):
1265
1306
  ).validated_save()
1266
1307
 
1267
1308
  def test_relationship(self):
1268
- params = {"relationship": [self.relationships[0].slug]}
1309
+ params = {"relationship": [self.relationships[0].key]}
1269
1310
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
1270
1311
 
1271
1312
  def test_source_type(self):
@@ -1291,7 +1332,6 @@ class RelationshipAssociationTestCase(FilterTestCases.FilterTestCase):
1291
1332
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1292
1333
 
1293
1334
 
1294
- @skip(reason="Content Types are BROKEN")
1295
1335
  class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1296
1336
  queryset = RelationshipAssociation.objects.all()
1297
1337
  filterset = RelationshipAssociationFilterSet
@@ -1302,22 +1342,22 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1302
1342
  cls.vlan_type = ContentType.objects.get_for_model(VLAN)
1303
1343
  cls.relationships = (
1304
1344
  Relationship(
1305
- name="Device VLANs",
1306
- slug="device-vlans",
1345
+ label="Device VLANs",
1346
+ key="device_vlans",
1307
1347
  type="many-to-many",
1308
1348
  source_type=cls.device_type,
1309
1349
  destination_type=cls.vlan_type,
1310
1350
  ),
1311
1351
  Relationship(
1312
- name="Primary VLAN",
1313
- slug="primary-vlan",
1352
+ label="Primary VLAN",
1353
+ key="primary_vlan",
1314
1354
  type="one-to-many",
1315
1355
  source_type=cls.vlan_type,
1316
1356
  destination_type=cls.device_type,
1317
1357
  ),
1318
1358
  Relationship(
1319
- name="Device Peers",
1320
- slug="device-peers",
1359
+ label="Device Peers",
1360
+ key="device_peers",
1321
1361
  type="symmetric-many-to-many",
1322
1362
  source_type=cls.device_type,
1323
1363
  destination_type=cls.device_type,
@@ -1329,16 +1369,24 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1329
1369
  manufacturer = Manufacturer.objects.first()
1330
1370
  devicetype = DeviceType.objects.create(manufacturer=manufacturer, model="Device Type 1", slug="device-type-1")
1331
1371
  devicerole = Role.objects.get_for_model(Device).first()
1372
+ devicestatus = Status.objects.get_for_model(Device).first()
1332
1373
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1333
1374
  cls.devices = (
1334
- Device.objects.create(name="Device 1", device_type=devicetype, role=devicerole, location=location),
1335
- Device.objects.create(name="Device 2", device_type=devicetype, role=devicerole, location=location),
1336
- Device.objects.create(name="Device 3", device_type=devicetype, role=devicerole, location=location),
1375
+ Device.objects.create(
1376
+ name="Device 1", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1377
+ ),
1378
+ Device.objects.create(
1379
+ name="Device 2", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1380
+ ),
1381
+ Device.objects.create(
1382
+ name="Device 3", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1383
+ ),
1337
1384
  )
1385
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1338
1386
  cls.vlans = (
1339
- VLAN.objects.create(vid=1, name="VLAN 1"),
1340
- VLAN.objects.create(vid=2, name="VLAN 2"),
1341
- VLAN.objects.create(vid=3, name="VLAN 3"),
1387
+ VLAN.objects.create(vid=1, name="VLAN 1", status=vlan_status),
1388
+ VLAN.objects.create(vid=2, name="VLAN 2", status=vlan_status),
1389
+ VLAN.objects.create(vid=3, name="VLAN 3", status=vlan_status),
1342
1390
  )
1343
1391
  cls.relationship_associations = (
1344
1392
  RelationshipAssociation(
@@ -1405,7 +1453,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1405
1453
  self.queryset = Device.objects.all()
1406
1454
  self.filterset = DeviceFilterSet
1407
1455
  self.assertEqual(
1408
- self.filterset({f"cr_{self.relationships[1].slug}__source": [self.vlans[0].pk]}, self.queryset).qs.count(),
1456
+ self.filterset({f"cr_{self.relationships[1].key}__source": [self.vlans[0].pk]}, self.queryset).qs.count(),
1409
1457
  3,
1410
1458
  )
1411
1459
 
@@ -1414,7 +1462,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1414
1462
  self.filterset = VLANFilterSet
1415
1463
  self.assertEqual(
1416
1464
  self.filterset(
1417
- {f"cr_{self.relationships[1].slug}__destination": [self.devices[0].pk, self.devices[1].pk]},
1465
+ {f"cr_{self.relationships[1].key}__destination": [self.devices[0].pk, self.devices[1].pk]},
1418
1466
  self.queryset,
1419
1467
  ).qs.count(),
1420
1468
  1,
@@ -1425,7 +1473,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1425
1473
  self.filterset = VLANFilterSet
1426
1474
  self.assertEqual(
1427
1475
  self.filterset(
1428
- {f"cr_{self.relationships[0].slug}__source": [self.devices[0].pk, self.devices[1].pk]}, self.queryset
1476
+ {f"cr_{self.relationships[0].key}__source": [self.devices[0].pk, self.devices[1].pk]}, self.queryset
1429
1477
  ).qs.count(),
1430
1478
  2,
1431
1479
  )
@@ -1435,7 +1483,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1435
1483
  self.filterset = DeviceFilterSet
1436
1484
  self.assertEqual(
1437
1485
  self.filterset(
1438
- {f"cr_{self.relationships[0].slug}__destination": [self.vlans[0].pk, self.vlans[1].pk]}, self.queryset
1486
+ {f"cr_{self.relationships[0].key}__destination": [self.vlans[0].pk, self.vlans[1].pk]}, self.queryset
1439
1487
  ).qs.count(),
1440
1488
  2,
1441
1489
  )
@@ -1445,12 +1493,12 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1445
1493
  self.filterset = DeviceFilterSet
1446
1494
  self.assertEqual(
1447
1495
  self.filterset(
1448
- {f"cr_{self.relationships[2].slug}__peer": [self.devices[0].pk, self.devices[1].pk]}, self.queryset
1496
+ {f"cr_{self.relationships[2].key}__peer": [self.devices[0].pk, self.devices[1].pk]}, self.queryset
1449
1497
  ).qs.count(),
1450
1498
  3,
1451
1499
  )
1452
1500
  self.assertEqual(
1453
- self.filterset({f"cr_{self.relationships[2].slug}__peer": [self.devices[2].pk]}, self.queryset).qs.count(),
1501
+ self.filterset({f"cr_{self.relationships[2].key}__peer": [self.devices[2].pk]}, self.queryset).qs.count(),
1454
1502
  2,
1455
1503
  )
1456
1504
 
@@ -1460,8 +1508,8 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1460
1508
  self.assertEqual(
1461
1509
  self.filterset(
1462
1510
  {
1463
- f"cr_{self.relationships[2].slug}__peer": [self.devices[0].pk, self.devices[1].pk],
1464
- f"cr_{self.relationships[0].slug}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1511
+ f"cr_{self.relationships[2].key}__peer": [self.devices[0].pk, self.devices[1].pk],
1512
+ f"cr_{self.relationships[0].key}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1465
1513
  },
1466
1514
  self.queryset,
1467
1515
  ).qs.count(),
@@ -1470,8 +1518,8 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1470
1518
  self.assertEqual(
1471
1519
  self.filterset(
1472
1520
  {
1473
- f"cr_{self.relationships[2].slug}__peer": [self.devices[2].pk],
1474
- f"cr_{self.relationships[0].slug}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1521
+ f"cr_{self.relationships[2].key}__peer": [self.devices[2].pk],
1522
+ f"cr_{self.relationships[0].key}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1475
1523
  },
1476
1524
  self.queryset,
1477
1525
  ).qs.count(),
@@ -1490,7 +1538,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1490
1538
  self.assertEqual(
1491
1539
  self.filterset(
1492
1540
  {
1493
- f"cr_{self.relationships[0].slug}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1541
+ f"cr_{self.relationships[0].key}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1494
1542
  "manufacturer": ["manufacturer-1"],
1495
1543
  },
1496
1544
  self.queryset,