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
@@ -50,32 +50,37 @@ from nautobot.tenancy.models import Tenant
50
50
 
51
51
 
52
52
  class CableLengthTestCase(TestCase):
53
- def setUp(self):
54
- self.location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
55
- self.manufacturer = Manufacturer.objects.first()
56
- self.devicetype = DeviceType.objects.create(
57
- manufacturer=self.manufacturer,
53
+ @classmethod
54
+ def setUpTestData(cls):
55
+ cls.location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
56
+ cls.manufacturer = Manufacturer.objects.first()
57
+ cls.devicetype = DeviceType.objects.create(
58
+ manufacturer=cls.manufacturer,
58
59
  model="Test Device Type 1",
59
60
  slug="test-device-type-1",
60
61
  )
61
- self.devicerole = Role.objects.get_for_model(Device).first()
62
- self.device1 = Device.objects.create(
63
- device_type=self.devicetype,
64
- role=self.devicerole,
62
+ cls.devicerole = Role.objects.get_for_model(Device).first()
63
+ devicestatus = Status.objects.get_for_model(Device).first()
64
+ cls.device1 = Device.objects.create(
65
+ device_type=cls.devicetype,
66
+ role=cls.devicerole,
65
67
  name="TestDevice1",
66
- location=self.location,
68
+ location=cls.location,
69
+ status=devicestatus,
67
70
  )
68
- self.device2 = Device.objects.create(
69
- device_type=self.devicetype,
70
- role=self.devicerole,
71
+ cls.device2 = Device.objects.create(
72
+ device_type=cls.devicetype,
73
+ role=cls.devicerole,
71
74
  name="TestDevice2",
72
- location=self.location,
75
+ location=cls.location,
76
+ status=devicestatus,
73
77
  )
74
- self.status = Status.objects.get_for_model(Cable).get(name="Connected")
78
+ cls.status = Status.objects.get_for_model(Cable).get(name="Connected")
75
79
 
76
80
  def test_cable_validated_save(self):
77
- interface1 = Interface.objects.create(device=self.device1, name="eth0")
78
- interface2 = Interface.objects.create(device=self.device2, name="eth0")
81
+ interfacestatus = Status.objects.get_for_model(Interface).first()
82
+ interface1 = Interface.objects.create(device=self.device1, name="eth0", status=interfacestatus)
83
+ interface2 = Interface.objects.create(device=self.device2, name="eth0", status=interfacestatus)
79
84
  cable = Cable(
80
85
  termination_a=interface1,
81
86
  termination_b=interface2,
@@ -87,8 +92,9 @@ class CableLengthTestCase(TestCase):
87
92
  cable.validated_save()
88
93
 
89
94
  def test_cable_full_clean(self):
90
- interface3 = Interface.objects.create(device=self.device1, name="eth1")
91
- interface4 = Interface.objects.create(device=self.device2, name="eth1")
95
+ interfacestatus = Status.objects.get_for_model(Interface).first()
96
+ interface3 = Interface.objects.create(device=self.device1, name="eth1", status=interfacestatus)
97
+ interface4 = Interface.objects.create(device=self.device2, name="eth1", status=interfacestatus)
92
98
  cable = Cable(
93
99
  termination_a=interface3,
94
100
  termination_b=interface4,
@@ -195,7 +201,8 @@ class InterfaceTemplateTestCase(TestCase):
195
201
  class RackGroupTestCase(ModelTestCases.BaseModelTestCase):
196
202
  model = RackGroup
197
203
 
198
- def setUp(self):
204
+ @classmethod
205
+ def setUpTestData(cls):
199
206
  """
200
207
  Location A
201
208
  - RackGroup A1
@@ -204,25 +211,33 @@ class RackGroupTestCase(ModelTestCases.BaseModelTestCase):
204
211
  - Rack 1
205
212
  - PowerPanel 1
206
213
  """
207
- self.location_type_a = LocationType.objects.get(name="Campus")
208
- self.location_a = Location.objects.filter(location_type=self.location_type_a).first()
209
- self.rackgroup_a1 = RackGroup(location=self.location_a, name="RackGroup A1", slug="rackgroup-a1")
210
- self.rackgroup_a1.save()
211
- self.rackgroup_a2 = RackGroup(location=self.location_a, parent=self.rackgroup_a1, name="RackGroup A2")
212
- self.rackgroup_a2.save()
214
+ cls.location_type_a = LocationType.objects.get(name="Campus")
215
+ cls.location_a = Location.objects.filter(location_type=cls.location_type_a).first()
216
+ cls.location_status = Status.objects.get_for_model(Location).first()
217
+ cls.rackgroup_a1 = RackGroup(location=cls.location_a, name="RackGroup A1", slug="rackgroup-a1")
218
+ cls.rackgroup_a1.save()
219
+ cls.rackgroup_a2 = RackGroup(location=cls.location_a, parent=cls.rackgroup_a1, name="RackGroup A2")
220
+ cls.rackgroup_a2.save()
213
221
 
214
- self.rack1 = Rack.objects.create(location=self.location_a, rack_group=self.rackgroup_a1, name="Rack 1")
215
- self.rack2 = Rack.objects.create(location=self.location_a, rack_group=self.rackgroup_a2, name="Rack 2")
222
+ rack_status = Status.objects.get_for_model(Rack).first()
223
+ cls.rack1 = Rack.objects.create(
224
+ location=cls.location_a, rack_group=cls.rackgroup_a1, name="Rack 1", status=rack_status
225
+ )
226
+ cls.rack2 = Rack.objects.create(
227
+ location=cls.location_a, rack_group=cls.rackgroup_a2, name="Rack 2", status=rack_status
228
+ )
216
229
 
217
- self.powerpanel1 = PowerPanel.objects.create(
218
- location=self.location_a, rack_group=self.rackgroup_a1, name="Power Panel 1"
230
+ cls.powerpanel1 = PowerPanel.objects.create(
231
+ location=cls.location_a, rack_group=cls.rackgroup_a1, name="Power Panel 1"
219
232
  )
220
233
 
221
234
  def test_rackgroup_location_validation(self):
222
235
  """Check that rack group locations are validated correctly."""
223
236
  # Group location, if specified, must permit RackGroups
224
237
  location_type_c = LocationType.objects.get(name="Elevator")
225
- location_c = Location.objects.create(name="Location C", location_type=location_type_c)
238
+ location_c = Location.objects.create(
239
+ name="Location C", location_type=location_type_c, status=self.location_status
240
+ )
226
241
  child = RackGroup(parent=self.rackgroup_a1, location=location_c, name="Child Group")
227
242
  with self.assertRaises(ValidationError) as cm:
228
243
  child.validated_save()
@@ -231,7 +246,9 @@ class RackGroupTestCase(ModelTestCases.BaseModelTestCase):
231
246
  # Child group location must descend from parent group location
232
247
  location_type_d = LocationType.objects.get(name="Room")
233
248
  location_type_d.content_types.add(ContentType.objects.get_for_model(RackGroup))
234
- location_d = Location.objects.create(name="Location D", location_type=location_type_d, parent=location_c)
249
+ location_d = Location.objects.create(
250
+ name="Location D", location_type=location_type_d, parent=location_c, status=self.location_status
251
+ )
235
252
  child = RackGroup(parent=self.rackgroup_a1, location=location_d, name="Child Group")
236
253
  with self.assertRaises(ValidationError) as cm:
237
254
  child.validated_save()
@@ -246,7 +263,9 @@ class RackGroupTestCase(ModelTestCases.BaseModelTestCase):
246
263
 
247
264
  In this test, the new Location permits Racks and PowerPanels so the Location should match.
248
265
  """
249
- location_b = Location.objects.create(name="Location B", location_type=self.location_type_a)
266
+ location_b = Location.objects.create(
267
+ name="Location B", location_type=self.location_type_a, status=self.location_status
268
+ )
250
269
 
251
270
  self.rackgroup_a1.location = location_b
252
271
  self.rackgroup_a1.save()
@@ -265,7 +284,9 @@ class RackGroupTestCase(ModelTestCases.BaseModelTestCase):
265
284
  """
266
285
  location_type_c = LocationType.objects.create(name="Location Type C", parent=self.location_type_a)
267
286
  location_type_c.content_types.add(ContentType.objects.get_for_model(RackGroup))
268
- location_c = Location.objects.create(name="Location C", location_type=location_type_c, parent=self.location_a)
287
+ location_c = Location.objects.create(
288
+ name="Location C", location_type=location_type_c, parent=self.location_a, status=self.location_status
289
+ )
269
290
 
270
291
  self.rackgroup_a1.location = location_c
271
292
  with self.assertRaises(ValidationError) as cm:
@@ -280,44 +301,51 @@ class RackGroupTestCase(ModelTestCases.BaseModelTestCase):
280
301
  class RackTestCase(ModelTestCases.BaseModelTestCase):
281
302
  model = Rack
282
303
 
283
- def setUp(self):
284
- self.status = Status.objects.get_for_model(Rack).first()
285
- self.location_type_a = LocationType.objects.create(name="Location Type A")
286
- self.location_type_a.content_types.add(
304
+ @classmethod
305
+ def setUpTestData(cls):
306
+ cls.status = Status.objects.get_for_model(Rack).first()
307
+ cls.location_type_a = LocationType.objects.create(name="Location Type A")
308
+ cls.location_type_a.content_types.add(
287
309
  ContentType.objects.get_for_model(RackGroup),
288
310
  ContentType.objects.get_for_model(Rack),
289
311
  ContentType.objects.get_for_model(Device),
290
312
  )
291
313
 
292
- self.location1 = Location.objects.create(name="Location1", location_type=self.location_type_a)
293
- self.location2 = Location.objects.create(name="Location2", location_type=self.location_type_a)
294
- self.group1 = RackGroup.objects.create(name="TestGroup1", slug="test-group-1", location=self.location1)
295
- self.group2 = RackGroup.objects.create(name="TestGroup2", slug="test-group-2", location=self.location2)
296
- self.rack = Rack.objects.create(
314
+ cls.location_status = Status.objects.get_for_model(Location).first()
315
+ cls.location1 = Location.objects.create(
316
+ name="Location1", location_type=cls.location_type_a, status=cls.location_status
317
+ )
318
+ cls.location2 = Location.objects.create(
319
+ name="Location2", location_type=cls.location_type_a, status=cls.location_status
320
+ )
321
+ cls.group1 = RackGroup.objects.create(name="TestGroup1", slug="test-group-1", location=cls.location1)
322
+ cls.group2 = RackGroup.objects.create(name="TestGroup2", slug="test-group-2", location=cls.location2)
323
+ cls.rack = Rack.objects.create(
297
324
  name="TestRack1",
298
325
  facility_id="A101",
299
- location=self.location1,
300
- rack_group=self.group1,
301
- status=self.status,
326
+ location=cls.location1,
327
+ rack_group=cls.group1,
328
+ status=cls.status,
302
329
  u_height=42,
303
330
  )
304
- self.manufacturer = Manufacturer.objects.first()
331
+ cls.manufacturer = Manufacturer.objects.first()
305
332
 
306
- self.device_type = {
333
+ cls.device_type = {
307
334
  "ff2048": DeviceType.objects.create(
308
- manufacturer=self.manufacturer,
335
+ manufacturer=cls.manufacturer,
309
336
  model="FrameForwarder 2048",
310
337
  slug="ff2048",
311
338
  ),
312
339
  "cc5000": DeviceType.objects.create(
313
- manufacturer=self.manufacturer,
340
+ manufacturer=cls.manufacturer,
314
341
  model="CurrentCatapult 5000",
315
342
  slug="cc5000",
316
343
  u_height=0,
317
344
  ),
318
345
  }
319
- self.roles = Role.objects.get_for_model(Rack)
320
- self.device_roles = Role.objects.get_for_model(Device)
346
+ cls.roles = Role.objects.get_for_model(Rack)
347
+ cls.device_roles = Role.objects.get_for_model(Device)
348
+ cls.device_status = Status.objects.get_for_model(Device).first()
321
349
 
322
350
  def test_rack_device_outside_height(self):
323
351
  rack1 = Rack(
@@ -333,6 +361,7 @@ class RackTestCase(ModelTestCases.BaseModelTestCase):
333
361
  name="TestSwitch1",
334
362
  device_type=self.device_type["ff2048"],
335
363
  role=self.device_roles[0],
364
+ status=self.device_status,
336
365
  location=self.location1,
337
366
  rack=rack1,
338
367
  position=43,
@@ -348,6 +377,7 @@ class RackTestCase(ModelTestCases.BaseModelTestCase):
348
377
  name="TestSwitch1",
349
378
  device_type=self.device_type["ff2048"],
350
379
  role=self.device_roles[1],
380
+ status=self.device_status,
351
381
  location=self.location1,
352
382
  rack=self.rack,
353
383
  position=10,
@@ -376,6 +406,7 @@ class RackTestCase(ModelTestCases.BaseModelTestCase):
376
406
  pdu = Device.objects.create(
377
407
  name="TestPDU",
378
408
  role=self.device_roles[3],
409
+ status=self.device_status,
379
410
  device_type=self.device_type.get("cc5000"),
380
411
  location=self.location1,
381
412
  rack=self.rack,
@@ -396,6 +427,7 @@ class RackTestCase(ModelTestCases.BaseModelTestCase):
396
427
  rack=self.rack,
397
428
  device_type=self.device_type["cc5000"],
398
429
  role=self.device_roles[3],
430
+ status=self.device_status,
399
431
  )
400
432
  # Device2 is explicitly assigned to the same location as the Rack
401
433
  device2 = Device.objects.create(
@@ -403,15 +435,15 @@ class RackTestCase(ModelTestCases.BaseModelTestCase):
403
435
  rack=self.rack,
404
436
  device_type=self.device_type["cc5000"],
405
437
  role=self.device_roles[3],
438
+ status=self.device_status,
406
439
  )
407
440
 
408
441
  # Move self.rack to a new location
409
- location2 = Location.objects.create(name="Location2", location_type=self.location_type_a)
410
- self.rack.location = location2
442
+ self.rack.location = self.location2
411
443
  self.rack.save()
412
444
 
413
- self.assertEqual(Device.objects.get(pk=device1.pk).location, location2)
414
- self.assertEqual(Device.objects.get(pk=device2.pk).location, location2)
445
+ self.assertEqual(Device.objects.get(pk=device1.pk).location, self.location2)
446
+ self.assertEqual(Device.objects.get(pk=device2.pk).location, self.location2)
415
447
 
416
448
  def test_change_rack_location_devices_not_permitted(self):
417
449
  """
@@ -424,21 +456,23 @@ class RackTestCase(ModelTestCases.BaseModelTestCase):
424
456
  rack=self.rack,
425
457
  device_type=self.device_type["cc5000"],
426
458
  role=self.device_roles[3],
459
+ status=self.device_status,
427
460
  )
428
461
 
429
462
  # Move self.rack to a new location that permits Racks but not Devices
430
463
  location_type_b = LocationType.objects.create(name="Location Type B")
431
464
  location_type_b.content_types.add(ContentType.objects.get_for_model(Rack))
432
- location2 = Location.objects.create(name="Location2", location_type=location_type_b)
433
- self.rack.location = location2
465
+ location3 = Location.objects.create(
466
+ name="Location3", location_type=location_type_b, status=self.location_status
467
+ )
468
+ self.rack.location = location3
434
469
  with self.assertRaises(ValidationError) as cm:
435
470
  self.rack.save()
436
471
  self.assertIn(f'Devices may not associate to locations of type "{location_type_b}"', str(cm.exception))
437
472
 
438
473
  def test_rack_location_validation(self):
439
474
  # Rack group location and rack location must relate
440
- location2 = Location.objects.create(name="Location2", location_type=self.location_type_a)
441
- rack = Rack(name="Rack", rack_group=self.group1, location=location2, status=self.status)
475
+ rack = Rack(name="Rack", rack_group=self.group1, location=self.location2, status=self.status)
442
476
  with self.assertRaises(ValidationError) as cm:
443
477
  rack.validated_save()
444
478
  self.assertIn(
@@ -448,7 +482,9 @@ class RackTestCase(ModelTestCases.BaseModelTestCase):
448
482
 
449
483
  # Location type must permit Racks
450
484
  location_type_b = LocationType.objects.create(name="Location Type B")
451
- locationb = Location.objects.create(name="Location2", location_type=location_type_b)
485
+ locationb = Location.objects.create(
486
+ name="Location3", location_type=location_type_b, status=self.location_status
487
+ )
452
488
  rack = Rack(name="Rack", location=locationb, status=self.status)
453
489
  with self.assertRaises(ValidationError) as cm:
454
490
  rack.validated_save()
@@ -864,7 +900,8 @@ class DeviceTestCase(ModelTestCases.BaseModelTestCase):
864
900
  class CableTestCase(ModelTestCases.BaseModelTestCase):
865
901
  model = Cable
866
902
 
867
- def setUp(self):
903
+ @classmethod
904
+ def setUpTestData(cls):
868
905
  location = Location.objects.first()
869
906
  manufacturer = Manufacturer.objects.first()
870
907
  devicetype = DeviceType.objects.create(
@@ -873,81 +910,91 @@ class CableTestCase(ModelTestCases.BaseModelTestCase):
873
910
  slug="test-device-type-1",
874
911
  )
875
912
  devicerole = Role.objects.get_for_model(Device).first()
876
- self.device1 = Device.objects.create(
913
+ devicestatus = Status.objects.get_for_model(Device).first()
914
+ cls.device1 = Device.objects.create(
877
915
  device_type=devicetype,
878
916
  role=devicerole,
879
917
  name="TestDevice1",
880
918
  location=location,
919
+ status=devicestatus,
881
920
  )
882
- self.device2 = Device.objects.create(
921
+ cls.device2 = Device.objects.create(
883
922
  device_type=devicetype,
884
923
  role=devicerole,
885
924
  name="TestDevice2",
886
925
  location=location,
887
- )
888
- self.interface1 = Interface.objects.create(device=self.device1, name="eth0")
889
- self.interface2 = Interface.objects.create(device=self.device2, name="eth0")
890
- self.interface3 = Interface.objects.create(device=self.device2, name="eth1")
891
- self.status = Status.objects.get_for_model(Cable).get(name="Connected")
892
- self.cable = Cable(
893
- termination_a=self.interface1,
894
- termination_b=self.interface2,
895
- status=self.status,
896
- )
897
- self.cable.save()
898
-
899
- self.power_port1 = PowerPort.objects.create(device=self.device2, name="psu1")
900
- self.patch_panel = Device.objects.create(
926
+ status=devicestatus,
927
+ )
928
+ interfacestatus = Status.objects.get_for_model(Interface).first()
929
+ cls.interface1 = Interface.objects.create(device=cls.device1, name="eth0", status=interfacestatus)
930
+ cls.interface2 = Interface.objects.create(device=cls.device2, name="eth0", status=interfacestatus)
931
+ cls.interface3 = Interface.objects.create(device=cls.device2, name="eth1", status=interfacestatus)
932
+ cls.status = Status.objects.get_for_model(Cable).get(name="Connected")
933
+ cls.cable = Cable(
934
+ termination_a=cls.interface1,
935
+ termination_b=cls.interface2,
936
+ status=cls.status,
937
+ )
938
+ cls.cable.save()
939
+
940
+ cls.power_port1 = PowerPort.objects.create(device=cls.device2, name="psu1")
941
+ cls.patch_panel = Device.objects.create(
901
942
  device_type=devicetype,
902
943
  role=devicerole,
903
944
  name="TestPatchPanel",
904
945
  location=location,
946
+ status=devicestatus,
905
947
  )
906
- self.rear_port1 = RearPort.objects.create(device=self.patch_panel, name="RP1", type="8p8c")
907
- self.front_port1 = FrontPort.objects.create(
908
- device=self.patch_panel,
948
+ cls.rear_port1 = RearPort.objects.create(device=cls.patch_panel, name="RP1", type="8p8c")
949
+ cls.front_port1 = FrontPort.objects.create(
950
+ device=cls.patch_panel,
909
951
  name="FP1",
910
952
  type="8p8c",
911
- rear_port=self.rear_port1,
953
+ rear_port=cls.rear_port1,
912
954
  rear_port_position=1,
913
955
  )
914
- self.rear_port2 = RearPort.objects.create(device=self.patch_panel, name="RP2", type="8p8c", positions=2)
915
- self.front_port2 = FrontPort.objects.create(
916
- device=self.patch_panel,
956
+ cls.rear_port2 = RearPort.objects.create(device=cls.patch_panel, name="RP2", type="8p8c", positions=2)
957
+ cls.front_port2 = FrontPort.objects.create(
958
+ device=cls.patch_panel,
917
959
  name="FP2",
918
960
  type="8p8c",
919
- rear_port=self.rear_port2,
961
+ rear_port=cls.rear_port2,
920
962
  rear_port_position=1,
921
963
  )
922
- self.rear_port3 = RearPort.objects.create(device=self.patch_panel, name="RP3", type="8p8c", positions=3)
923
- self.front_port3 = FrontPort.objects.create(
924
- device=self.patch_panel,
964
+ cls.rear_port3 = RearPort.objects.create(device=cls.patch_panel, name="RP3", type="8p8c", positions=3)
965
+ cls.front_port3 = FrontPort.objects.create(
966
+ device=cls.patch_panel,
925
967
  name="FP3",
926
968
  type="8p8c",
927
- rear_port=self.rear_port3,
969
+ rear_port=cls.rear_port3,
928
970
  rear_port_position=1,
929
971
  )
930
- self.rear_port4 = RearPort.objects.create(device=self.patch_panel, name="RP4", type="8p8c", positions=3)
931
- self.front_port4 = FrontPort.objects.create(
932
- device=self.patch_panel,
972
+ cls.rear_port4 = RearPort.objects.create(device=cls.patch_panel, name="RP4", type="8p8c", positions=3)
973
+ cls.front_port4 = FrontPort.objects.create(
974
+ device=cls.patch_panel,
933
975
  name="FP4",
934
976
  type="8p8c",
935
- rear_port=self.rear_port4,
977
+ rear_port=cls.rear_port4,
936
978
  rear_port_position=1,
937
979
  )
938
- self.provider = Provider.objects.first()
939
- provider_network = ProviderNetwork.objects.create(name="Provider Network 1", provider=self.provider)
940
- self.circuittype = CircuitType.objects.first()
941
- self.circuit1 = Circuit.objects.create(provider=self.provider, circuit_type=self.circuittype, cid="1")
942
- self.circuit2 = Circuit.objects.create(provider=self.provider, circuit_type=self.circuittype, cid="2")
943
- self.circuittermination1 = CircuitTermination.objects.create(
944
- circuit=self.circuit1, location=location, term_side="A"
980
+ cls.provider = Provider.objects.first()
981
+ provider_network = ProviderNetwork.objects.create(name="Provider Network 1", provider=cls.provider)
982
+ cls.circuittype = CircuitType.objects.first()
983
+ circuit_status = Status.objects.get_for_model(Circuit).first()
984
+ cls.circuit1 = Circuit.objects.create(
985
+ provider=cls.provider, circuit_type=cls.circuittype, cid="1", status=circuit_status
986
+ )
987
+ cls.circuit2 = Circuit.objects.create(
988
+ provider=cls.provider, circuit_type=cls.circuittype, cid="2", status=circuit_status
989
+ )
990
+ cls.circuittermination1 = CircuitTermination.objects.create(
991
+ circuit=cls.circuit1, location=location, term_side="A"
945
992
  )
946
- self.circuittermination2 = CircuitTermination.objects.create(
947
- circuit=self.circuit1, location=location, term_side="Z"
993
+ cls.circuittermination2 = CircuitTermination.objects.create(
994
+ circuit=cls.circuit1, location=location, term_side="Z"
948
995
  )
949
- self.circuittermination3 = CircuitTermination.objects.create(
950
- circuit=self.circuit2, provider_network=provider_network, term_side="Z"
996
+ cls.circuittermination3 = CircuitTermination.objects.create(
997
+ circuit=cls.circuit2, provider_network=provider_network, term_side="Z"
951
998
  )
952
999
 
953
1000
  def test_cable_creation(self):
@@ -1105,16 +1152,19 @@ class CableTestCase(ModelTestCases.BaseModelTestCase):
1105
1152
  Status.objects.get(name=connected_status_name).delete()
1106
1153
  device = Device.objects.first()
1107
1154
 
1155
+ interface_status = Status.objects.get_for_model(Interface).first()
1108
1156
  interfaces = (
1109
1157
  Interface.objects.create(
1110
1158
  device=device,
1111
1159
  name="eth-0",
1112
1160
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
1161
+ status=interface_status,
1113
1162
  ),
1114
1163
  Interface.objects.create(
1115
1164
  device=device,
1116
1165
  name="eth-1",
1117
1166
  type=InterfaceTypeChoices.TYPE_1GE_FIXED,
1167
+ status=interface_status,
1118
1168
  ),
1119
1169
  )
1120
1170
 
@@ -1122,6 +1172,7 @@ class CableTestCase(ModelTestCases.BaseModelTestCase):
1122
1172
  termination_a=interfaces[0],
1123
1173
  termination_b=interfaces[1],
1124
1174
  type=CableTypeChoices.TYPE_CAT6,
1175
+ status=Status.objects.get_for_model(Cable).first(),
1125
1176
  )
1126
1177
 
1127
1178
  self.assertTrue(Cable.objects.filter(id=cable.pk).exists())
@@ -1152,26 +1203,31 @@ class PowerPanelTestCase(TestCase): # TODO: change to BaseModelTestCase once we
1152
1203
 
1153
1204
 
1154
1205
  class InterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase once we have an InterfaceFactory
1155
- def setUp(self):
1206
+ @classmethod
1207
+ def setUpTestData(cls):
1156
1208
  manufacturer = Manufacturer.objects.first()
1157
1209
  devicetype = DeviceType.objects.create(manufacturer=manufacturer, model="Device Type 1", slug="device-type-1")
1158
1210
  devicerole = Role.objects.get_for_model(Device).first()
1159
1211
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1160
- self.vlan = VLAN.objects.create(name="VLAN 1", vid=100, location=location)
1212
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1213
+ cls.vlan = VLAN.objects.create(name="VLAN 1", vid=100, location=location, status=vlan_status)
1161
1214
  status = Status.objects.get_for_model(Device)[0]
1162
- self.device = Device.objects.create(
1215
+ cls.device = Device.objects.create(
1163
1216
  name="Device 1",
1164
1217
  device_type=devicetype,
1165
1218
  role=devicerole,
1166
1219
  location=location,
1167
1220
  status=status,
1168
1221
  )
1169
- self.other_location_vlan = VLAN.objects.create(
1222
+ cls.other_location_vlan = VLAN.objects.create(
1170
1223
  name="Other Location VLAN",
1171
1224
  vid=100,
1172
1225
  location=Location.objects.create(
1173
- name="Other Location", location_type=LocationType.objects.get(name="Campus")
1226
+ name="Other Location",
1227
+ location_type=LocationType.objects.get(name="Campus"),
1228
+ status=Status.objects.get_for_model(Location).first(),
1174
1229
  ),
1230
+ status=vlan_status,
1175
1231
  )
1176
1232
 
1177
1233
  def test_tagged_vlan_raise_error_if_mode_not_set_to_tagged(self):
@@ -1179,6 +1235,7 @@ class InterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase once we
1179
1235
  name="Int1",
1180
1236
  type=InterfaceTypeChoices.TYPE_VIRTUAL,
1181
1237
  device=self.device,
1238
+ status=Status.objects.get_for_model(Interface).first(),
1182
1239
  )
1183
1240
  with self.assertRaises(ValidationError) as err:
1184
1241
  interface.tagged_vlans.add(self.vlan)
@@ -1192,6 +1249,7 @@ class InterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase once we
1192
1249
  name="Test Interface",
1193
1250
  mode=InterfaceModeChoices.MODE_TAGGED,
1194
1251
  device=self.device,
1252
+ status=Status.objects.get_for_model(Interface).first(),
1195
1253
  )
1196
1254
  interface.tagged_vlans.add(self.other_location_vlan)
1197
1255
  self.assertEqual(
@@ -1206,6 +1264,7 @@ class InterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase once we
1206
1264
  name="Int1",
1207
1265
  type=InterfaceTypeChoices.TYPE_VIRTUAL,
1208
1266
  device=self.device,
1267
+ status=Status.objects.get_for_model(Interface).first(),
1209
1268
  )
1210
1269
  ips = list(IPAddress.objects.all()[:10])
1211
1270
 
@@ -1230,6 +1289,7 @@ class InterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase once we
1230
1289
  name="Int1",
1231
1290
  type=InterfaceTypeChoices.TYPE_VIRTUAL,
1232
1291
  device=self.device,
1292
+ status=Status.objects.get_for_model(Interface).first(),
1233
1293
  )
1234
1294
  ips = list(IPAddress.objects.all()[:10])
1235
1295
 
@@ -1252,3 +1312,15 @@ class InterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase once we
1252
1312
  count = interface.remove_ip_addresses(ips)
1253
1313
  self.assertEqual(count, 4)
1254
1314
  self.assertEqual(IPAddressToInterface.objects.filter(interface=interface).count(), 0)
1315
+
1316
+ # Test the pre_delete signal for IPAddressToInterface instances
1317
+ interface.add_ip_addresses(ips)
1318
+ self.device.primary_ip4 = interface.ip_addresses.all().filter(host__family=4).first()
1319
+ self.device.primary_ip6 = interface.ip_addresses.all().filter(host__family=6).first()
1320
+ self.device.save()
1321
+ interface.remove_ip_addresses(self.device.primary_ip4)
1322
+ self.device.refresh_from_db()
1323
+ self.assertEqual(self.device.primary_ip4, None)
1324
+ interface.remove_ip_addresses(self.device.primary_ip6)
1325
+ self.device.refresh_from_db()
1326
+ self.assertEqual(self.device.primary_ip6, None)
@@ -8,11 +8,12 @@ from nautobot.dcim.models import (
8
8
  LocationType,
9
9
  Manufacturer,
10
10
  )
11
- from nautobot.extras.models import Role
11
+ from nautobot.extras.models import Role, Status
12
12
 
13
13
 
14
14
  class NaturalOrderingTestCase(TestCase):
15
- def setUp(self):
15
+ @classmethod
16
+ def setUpTestData(cls):
16
17
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
17
18
  manufacturer = Manufacturer.objects.first()
18
19
  devicetype = DeviceType.objects.create(
@@ -21,9 +22,11 @@ class NaturalOrderingTestCase(TestCase):
21
22
  slug="test-device-type-1",
22
23
  )
23
24
  devicerole = Role.objects.get_for_model(Device).first()
24
- self.device = Device.objects.create(
25
- device_type=devicetype, role=devicerole, name="Test Device 1", location=location
25
+ devicestatus = Status.objects.get_for_model(Device).first()
26
+ cls.device = Device.objects.create(
27
+ device_type=devicetype, role=devicerole, name="Test Device 1", location=location, status=devicestatus
26
28
  )
29
+ cls.interface_status = Status.objects.get_for_model(Interface).first()
27
30
 
28
31
  def test_interface_ordering_numeric(self):
29
32
  INTERFACES = [
@@ -62,7 +65,7 @@ class NaturalOrderingTestCase(TestCase):
62
65
  ]
63
66
 
64
67
  for name in INTERFACES:
65
- iface = Interface(device=self.device, name=name)
68
+ iface = Interface(device=self.device, name=name, status=self.interface_status)
66
69
  iface.save()
67
70
 
68
71
  self.assertListEqual(
@@ -85,7 +88,7 @@ class NaturalOrderingTestCase(TestCase):
85
88
  ]
86
89
 
87
90
  for name in INTERFACES:
88
- iface = Interface(device=self.device, name=name)
91
+ iface = Interface(device=self.device, name=name, status=self.interface_status)
89
92
  iface.save()
90
93
 
91
94
  self.assertListEqual(
@@ -137,7 +140,7 @@ class NaturalOrderingTestCase(TestCase):
137
140
  ]
138
141
 
139
142
  for name in INTERFACES:
140
- iface = Interface(device=self.device, name=name)
143
+ iface = Interface(device=self.device, name=name, status=self.interface_status)
141
144
  iface.save()
142
145
 
143
146
  self.assertListEqual(
@@ -163,7 +166,7 @@ class NaturalOrderingTestCase(TestCase):
163
166
  ]
164
167
 
165
168
  for name in INTERFACES:
166
- iface = Interface(device=self.device, name=name)
169
+ iface = Interface(device=self.device, name=name, status=self.interface_status)
167
170
  iface.save()
168
171
 
169
172
  self.assertListEqual(