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,230 +1,85 @@
1
- from nautobot.core.apps import NavMenuAddButton, NavMenuGroup, NavMenuItem, NavMenuImportButton, NavMenuTab
1
+ from nautobot.core.apps import NavMenuGroup, NavMenuItem, NavMenuTab
2
2
 
3
3
 
4
4
  menu_items = (
5
5
  NavMenuTab(
6
- name="IPAM",
7
- weight=300,
6
+ name="Networks",
8
7
  groups=(
9
8
  NavMenuGroup(
10
- name="Namespaces",
11
- weight=50,
12
- items=(
13
- NavMenuItem(
14
- link="ipam:namespace_list",
15
- name="Namespaces",
16
- weight=100,
17
- permissions=[
18
- "ipam.view_namespace",
19
- ],
20
- buttons=(
21
- NavMenuAddButton(
22
- link="ipam:namespace_add",
23
- permissions=[
24
- "ipam.add_namespace",
25
- ],
26
- ),
27
- ),
28
- ),
29
- ),
30
- ),
31
- NavMenuGroup(
32
- name="IP Addresses",
9
+ name="IP Management",
33
10
  weight=100,
34
11
  items=(
35
12
  NavMenuItem(
36
- link="ipam:ipaddress_list",
37
13
  name="IP Addresses",
38
14
  weight=100,
39
- permissions=[
40
- "ipam.view_ipaddress",
41
- ],
42
- buttons=(
43
- NavMenuAddButton(
44
- link="ipam:ipaddress_add",
45
- permissions=[
46
- "ipam.add_ipaddress",
47
- ],
48
- ),
49
- NavMenuImportButton(
50
- link="ipam:ipaddress_import",
51
- permissions=[
52
- "ipam.add_ipaddress",
53
- ],
54
- ),
55
- ),
15
+ link="ipam:ipaddress_list",
16
+ permissions=["ipam.view_ipaddress"],
56
17
  ),
57
- ),
58
- ),
59
- NavMenuGroup(
60
- name="Prefixes",
61
- weight=200,
62
- items=(
63
18
  NavMenuItem(
64
- link="ipam:prefix_list",
65
19
  name="Prefixes",
66
- weight=100,
67
- permissions=[
68
- "ipam.view_prefix",
69
- ],
70
- buttons=(
71
- NavMenuAddButton(
72
- link="ipam:prefix_add",
73
- permissions=[
74
- "ipam.add_prefix",
75
- ],
76
- ),
77
- NavMenuImportButton(
78
- link="ipam:prefix_import",
79
- permissions=[
80
- "ipam.add_prefix",
81
- ],
82
- ),
83
- ),
20
+ weight=200,
21
+ link="ipam:prefix_list",
22
+ permissions=["ipam.view_prefix"],
84
23
  ),
85
24
  NavMenuItem(
86
- link="ipam:rir_list",
87
25
  name="RIRs",
88
- weight=200,
89
- permissions=[
90
- "ipam.view_rir",
91
- ],
92
- buttons=(
93
- NavMenuAddButton(
94
- link="ipam:rir_add",
95
- permissions=[
96
- "ipam.add_rir",
97
- ],
98
- ),
99
- NavMenuImportButton(
100
- link="ipam:rir_import",
101
- permissions=[
102
- "ipam.add_rir",
103
- ],
104
- ),
105
- ),
26
+ weight=300,
27
+ link="ipam:rir_list",
28
+ permissions=["ipam.view_rir"],
106
29
  ),
107
30
  ),
108
31
  ),
109
32
  NavMenuGroup(
110
- name="VRFs",
111
- weight=400,
33
+ name="Layer 2 / Switching",
34
+ weight=200,
112
35
  items=(
113
36
  NavMenuItem(
114
- link="ipam:vrf_list",
115
- name="VRFs",
37
+ name="VLANs",
116
38
  weight=100,
117
- permissions=[
118
- "ipam.view_vrf",
119
- ],
120
- buttons=(
121
- NavMenuAddButton(
122
- link="ipam:vrf_add",
123
- permissions=[
124
- "ipam.add_vrf",
125
- ],
126
- ),
127
- NavMenuImportButton(
128
- link="ipam:vrf_import",
129
- permissions=[
130
- "ipam.add_vrf",
131
- ],
132
- ),
133
- ),
39
+ link="ipam:vlan_list",
40
+ permissions=["ipam.view_vlan"],
134
41
  ),
135
42
  NavMenuItem(
136
- link="ipam:routetarget_list",
137
- name="Route Targets",
43
+ name="VLAN Groups",
138
44
  weight=200,
139
- permissions=[
140
- "ipam.view_routetarget",
141
- ],
142
- buttons=(
143
- NavMenuAddButton(
144
- link="ipam:routetarget_add",
145
- permissions=[
146
- "ipam.add_routetarget",
147
- ],
148
- ),
149
- NavMenuImportButton(
150
- link="ipam:routetarget_import",
151
- permissions=[
152
- "ipam.add_routetarget",
153
- ],
154
- ),
155
- ),
45
+ link="ipam:vlangroup_list",
46
+ permissions=["ipam.view_vlangroup"],
156
47
  ),
157
48
  ),
158
49
  ),
159
50
  NavMenuGroup(
160
- name="VLANs",
161
- weight=500,
51
+ name="Layer 3 / Routing",
52
+ weight=300,
162
53
  items=(
163
54
  NavMenuItem(
164
- link="ipam:vlan_list",
165
- name="VLANs",
55
+ name="Namespaces",
166
56
  weight=100,
167
- permissions=[
168
- "ipam.view_vlan",
169
- ],
170
- buttons=(
171
- NavMenuAddButton(
172
- link="ipam:vlan_add",
173
- permissions=[
174
- "ipam.add_vlan",
175
- ],
176
- ),
177
- NavMenuImportButton(
178
- link="ipam:vlan_import",
179
- permissions=[
180
- "ipam.add_vlan",
181
- ],
182
- ),
183
- ),
57
+ link="ipam:namespace_list",
58
+ permissions=["ipam.namespace_vrf"],
184
59
  ),
185
60
  NavMenuItem(
186
- link="ipam:vlangroup_list",
187
- name="VLAN Groups",
61
+ name="VRFs",
188
62
  weight=200,
189
- permissions=[
190
- "ipam.view_vlangroup",
191
- ],
192
- buttons=(
193
- NavMenuAddButton(
194
- link="ipam:vlangroup_add",
195
- permissions=[
196
- "ipam.add_vlangroup",
197
- ],
198
- ),
199
- NavMenuImportButton(
200
- link="ipam:vlangroup_import",
201
- permissions=[
202
- "ipam.add_vlangroup",
203
- ],
204
- ),
205
- ),
63
+ link="ipam:vrf_list",
64
+ permissions=["ipam.view_vrf"],
65
+ ),
66
+ NavMenuItem(
67
+ name="Route Targets",
68
+ weight=300,
69
+ link="ipam:routetarget_list",
70
+ permissions=["ipam.view_routetarget"],
206
71
  ),
207
72
  ),
208
73
  ),
209
74
  NavMenuGroup(
210
75
  name="Services",
211
- weight=600,
76
+ weight=400,
212
77
  items=(
213
78
  NavMenuItem(
214
- link="ipam:service_list",
215
79
  name="Services",
216
80
  weight=100,
217
- permissions=[
218
- "ipam.view_service",
219
- ],
220
- buttons=(
221
- NavMenuImportButton(
222
- link="ipam:service_import",
223
- permissions=[
224
- "ipam.add_service",
225
- ],
226
- ),
227
- ),
81
+ link="ipam:service_list",
82
+ permissions=["ipam.view_service"],
228
83
  ),
229
84
  ),
230
85
  ),
@@ -252,7 +252,7 @@ class PrefixQuerySet(BaseNetworkQuerySet):
252
252
  """
253
253
  if prefix:
254
254
  _prefix = netaddr.IPNetwork(prefix)
255
- if str(_prefix) != prefix:
255
+ if str(_prefix) != str(prefix):
256
256
  raise self.model.DoesNotExist()
257
257
  last_ip = self._get_last_ip(_prefix)
258
258
  kwargs["prefix_length"] = _prefix.prefixlen
@@ -327,47 +327,48 @@ class PrefixQuerySet(BaseNetworkQuerySet):
327
327
  except netaddr.AddrFormatError as err:
328
328
  raise ValidationError({"cidr": f"{value} does not appear to be an IPv4 or IPv6 network."}) from err
329
329
 
330
- def get_closest_parent(self, cidr, namespace, prefix_length=0):
330
+ def get_closest_parent(self, cidr, namespace, max_prefix_length=0):
331
331
  """
332
332
  Return the closest matching parent Prefix for a `cidr` even if it doesn't exist in the database.
333
333
 
334
334
  Args:
335
335
  cidr (str): IPv4/IPv6 CIDR string
336
336
  namespace (Namespace): Namespace instance
337
- prefix_length (int): Maximum prefix length depth for closest parent lookup
337
+ max_prefix_length (int): Maximum prefix length depth for closest parent lookup
338
338
  """
339
339
  # Validate that it's a real CIDR
340
340
  cidr = self._validate_cidr(cidr)
341
- broadcast = cidr.broadcast or cidr.ip
341
+ broadcast = str(cidr.broadcast or cidr.ip)
342
342
  ip_version = cidr.version
343
343
 
344
344
  try:
345
- prefix_length = int(prefix_length)
345
+ max_prefix_length = int(max_prefix_length)
346
346
  except ValueError:
347
- raise ValidationError({"prefix_length": f"Invalid prefix_length: {prefix_length}."})
347
+ raise ValidationError({"max_prefix_length": f"Invalid prefix_length: {max_prefix_length}."})
348
348
 
349
349
  # Walk the supernets backwrds from smallest to largest prefix.
350
350
  try:
351
- supernets = cidr.supernet(prefixlen=prefix_length)
351
+ supernets = cidr.supernet(prefixlen=max_prefix_length)
352
352
  except ValueError as err:
353
- raise ValidationError({"prefix_length": str(err)})
353
+ raise ValidationError({"max_prefix_length": str(err)})
354
354
  else:
355
355
  supernets.reverse()
356
356
 
357
357
  # Enumerate all unique networks and prefixes
358
358
  networks = {str(s.network) for s in supernets}
359
- prefix_lengths = {s.prefixlen for s in supernets}
360
- del supernets # Free the memory because DevOps.
359
+ del supernets # Free the memory because it could be quite large.
361
360
 
362
361
  # Prepare the queryset filter
363
362
  lookup_kwargs = {
364
363
  "network__in": networks,
365
- "prefix_length__in": prefix_lengths,
364
+ # TODO(jathan): This might be flawed if an IPAddress has a prefix_length that excludes it from a
365
+ # parent that should otherwise contain it. If we encounter issues in the future for
366
+ # identifying closest parent prefixes, this might be a starting point.
367
+ "prefix_length__lte": cidr.prefixlen,
368
+ "broadcast__gte": broadcast,
366
369
  "ip_version": ip_version,
367
- "broadcast__gte": str(broadcast),
370
+ "namespace": namespace,
368
371
  }
369
- if namespace is not None:
370
- lookup_kwargs["namespace"] = namespace
371
372
 
372
373
  # Search for possible ancestors by network/prefix, returning them in reverse order, so that
373
374
  # we can choose the first one.
@@ -443,7 +444,7 @@ class IPAddressQuerySet(BaseNetworkQuerySet):
443
444
  masked_hosts = [bytes(netaddr.IPNetwork(val).ip) for val in networks if "/" in val]
444
445
  masked_prefixes = [netaddr.IPNetwork(val).prefixlen for val in networks if "/" in val]
445
446
  unmasked_hosts = [bytes(netaddr.IPNetwork(val).ip) for val in networks if "/" not in val]
446
- return self.filter(Q(host__in=masked_hosts, prefix_length__in=masked_prefixes) | Q(host__in=unmasked_hosts))
447
+ return self.filter(Q(host__in=masked_hosts, mask_length__in=masked_prefixes) | Q(host__in=unmasked_hosts))
447
448
 
448
449
  def get(self, *args, address=None, **kwargs):
449
450
  """
@@ -451,10 +452,8 @@ class IPAddressQuerySet(BaseNetworkQuerySet):
451
452
  """
452
453
  if address:
453
454
  address = netaddr.IPNetwork(address)
454
- last_ip = self._get_last_ip(address)
455
- kwargs["prefix_length"] = address.prefixlen
455
+ kwargs["mask_length"] = address.prefixlen
456
456
  kwargs["host"] = address.ip
457
- kwargs["broadcast"] = last_ip
458
457
  return super().get(*args, **kwargs)
459
458
 
460
459
  def filter(self, *args, address=None, **kwargs):
@@ -463,10 +462,8 @@ class IPAddressQuerySet(BaseNetworkQuerySet):
463
462
  """
464
463
  if address:
465
464
  address = netaddr.IPNetwork(address)
466
- last_ip = self._get_last_ip(address)
467
- kwargs["prefix_length"] = address.prefixlen
465
+ kwargs["mask_length"] = address.prefixlen
468
466
  kwargs["host"] = address.ip
469
- kwargs["broadcast"] = last_ip
470
467
  return super().filter(*args, **kwargs)
471
468
 
472
469
  def filter_address_or_pk_in(self, addresses, pk_values=None):
@@ -478,11 +475,9 @@ class IPAddressQuerySet(BaseNetworkQuerySet):
478
475
  q = Q()
479
476
  for _address in addresses:
480
477
  _address = netaddr.IPNetwork(_address)
481
- last_ip = self._get_last_ip(_address)
482
- prefix_length = _address.prefixlen
478
+ mask_length = _address.prefixlen
483
479
  host = _address.ip
484
- broadcast = last_ip
485
- q |= Q(prefix_length=prefix_length, host=host, broadcast=broadcast)
480
+ q |= Q(mask_length=mask_length, host=host)
486
481
 
487
482
  if pk_values is not None:
488
483
  q |= Q(pk__in=pk_values)
nautobot/ipam/signals.py CHANGED
@@ -1,12 +1,12 @@
1
1
  from django.core.exceptions import ValidationError
2
- from django.db.models.signals import pre_save, m2m_changed
2
+ from django.db.models.signals import m2m_changed, pre_delete, pre_save
3
3
  from django.dispatch import receiver
4
4
 
5
- from nautobot.ipam.models import VRF
5
+ from nautobot.ipam.models import IPAddressToInterface, VRF, VRFDeviceAssignment, VRFPrefixAssignment
6
6
 
7
7
 
8
- @receiver(pre_save, sender=VRF.devices.through)
9
- @receiver(pre_save, sender=VRF.prefixes.through)
8
+ @receiver(pre_save, sender=VRFDeviceAssignment)
9
+ @receiver(pre_save, sender=VRFPrefixAssignment)
10
10
  def ipam_object_saved(sender, instance, raw=False, **kwargs):
11
11
  """
12
12
  Forcibly call `full_clean()` when a new IPAM intermediate model is manually saved to prevent
@@ -17,7 +17,7 @@ def ipam_object_saved(sender, instance, raw=False, **kwargs):
17
17
  instance.full_clean()
18
18
 
19
19
 
20
- @receiver(m2m_changed, sender=VRF.prefixes.through)
20
+ @receiver(m2m_changed, sender=VRFPrefixAssignment)
21
21
  def vrf_prefix_associated(sender, instance, action, reverse, model, pk_set, **kwargs):
22
22
  """
23
23
  Disallow adding Prefixes if the namespace doesn't match.
@@ -29,7 +29,7 @@ def vrf_prefix_associated(sender, instance, action, reverse, model, pk_set, **kw
29
29
  raise ValidationError({"prefixes": "Prefix must match namespace of VRF"})
30
30
 
31
31
 
32
- @receiver(m2m_changed, sender=VRF.devices.through)
32
+ @receiver(m2m_changed, sender=VRFDeviceAssignment)
33
33
  def vrf_device_associated(sender, instance, action, reverse, model, pk_set, **kwargs):
34
34
  """
35
35
  Assert validation on m2m when devices are associated with a VRF.
@@ -44,3 +44,46 @@ def vrf_device_associated(sender, instance, action, reverse, model, pk_set, **kw
44
44
  else:
45
45
  for assignment in instance.vrf_assignments.iterator():
46
46
  assignment.validated_save()
47
+
48
+
49
+ @receiver(pre_delete, sender=IPAddressToInterface)
50
+ def ip_address_to_interface_pre_delete(instance, raw=False, **kwargs):
51
+ if raw:
52
+ return
53
+
54
+ # Check if the removed IPAddressToInterface instance contains an IPAddress
55
+ # that is the primary_v{version} of the host machine.
56
+
57
+ if getattr(instance, "interface"):
58
+ host = instance.interface.device
59
+ other_assignments_exist = (
60
+ IPAddressToInterface.objects.filter(interface__device=host, ip_address=instance.ip_address)
61
+ .exclude(id=instance.id)
62
+ .exists()
63
+ )
64
+ else:
65
+ host = instance.vm_interface.virtual_machine
66
+ other_assignments_exist = (
67
+ IPAddressToInterface.objects.filter(vm_interface__virtual_machine=host, ip_address=instance.ip_address)
68
+ .exclude(id=instance.id)
69
+ .exists()
70
+ )
71
+
72
+ # Only nullify the primary_ip field if no other interfaces/vm_interfaces have the ip_address
73
+ if not other_assignments_exist and instance.ip_address == host.primary_ip4:
74
+ host.primary_ip4 = None
75
+ elif not other_assignments_exist and instance.ip_address == host.primary_ip6:
76
+ host.primary_ip6 = None
77
+ host.save()
78
+
79
+
80
+ @receiver(pre_save, sender=IPAddressToInterface)
81
+ def ip_address_to_interface_assignment_created(sender, instance, raw=False, **kwargs):
82
+ """
83
+ Forcibly call `full_clean()` when a new `IPAddressToInterface` object
84
+ is manually created to prevent inadvertantly creating invalid IPAddressToInterface.
85
+ """
86
+ if raw:
87
+ return
88
+
89
+ instance.full_clean()
nautobot/ipam/tables.py CHANGED
@@ -263,14 +263,21 @@ class VRFDeviceAssignmentTable(BaseTable):
263
263
  """Table for displaying VRF Device Assignments with RD."""
264
264
 
265
265
  vrf = tables.Column(verbose_name="VRF", linkify=lambda record: record.vrf.get_absolute_url(), accessor="vrf.name")
266
- device = tables.Column(linkify=lambda record: record.device.get_absolute_url(), accessor="device.name")
266
+ device = tables.Column(
267
+ linkify=lambda record: record.device.get_absolute_url(), accessor="device.name", verbose_name="Device"
268
+ )
269
+ virtual_machine = tables.Column(
270
+ linkify=lambda record: record.virtual_machine.get_absolute_url(),
271
+ accessor="virtual_machine.name",
272
+ verbose_name="Virtual Machine",
273
+ )
267
274
  rd = tables.Column(verbose_name="VRF RD")
268
275
  tenant = TenantColumn(accessor="vrf.tenant")
269
276
 
270
277
  class Meta(BaseTable.Meta):
271
278
  model = VRFDeviceAssignment
272
279
  orderable = False
273
- fields = ("vrf", "device", "rd", "tenant")
280
+ fields = ("vrf", "device", "virtual_machine", "rd", "tenant")
274
281
 
275
282
 
276
283
  class VRFPrefixAssignmentTable(BaseTable):
@@ -442,7 +449,7 @@ class PrefixDetailTable(PrefixTable):
442
449
  class IPAddressTable(StatusTableMixin, RoleTableMixin, BaseTable):
443
450
  pk = ToggleColumn()
444
451
  address = tables.TemplateColumn(
445
- template_code=IPADDRESS_COPY_LINK, verbose_name="IP Address", order_by=("host", "prefix_length")
452
+ template_code=IPADDRESS_COPY_LINK, verbose_name="IP Address", order_by=("host", "mask_length")
446
453
  )
447
454
  # vrf = tables.TemplateColumn(template_code=VRF_LINK, verbose_name="VRF")
448
455
  tenant = TenantColumn()
@@ -0,0 +1,11 @@
1
+ {% extends 'ipam/namespace_retrieve.html' %}
2
+
3
+ {% block title %}{{ block.super }} - IP Addresses{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="row">
7
+ <div class="col-md-12">
8
+ {% include 'utilities/obj_table.html' with table=ip_address_table table_template='panel_table.html' heading='IP Addresses' bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %}
9
+ </div>
10
+ </div>
11
+ {% endblock %}
@@ -0,0 +1,11 @@
1
+ {% extends 'ipam/namespace_retrieve.html' %}
2
+
3
+ {% block title %}{{ block.super }} - Prefixes{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="row">
7
+ <div class="col-md-12">
8
+ {% include 'utilities/obj_table.html' with table=prefix_table table_template='panel_table.html' heading='Prefixes' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' %}
9
+ </div>
10
+ </div>
11
+ {% endblock %}
@@ -23,7 +23,20 @@
23
23
  </div>
24
24
  {% endblock content_left_page %}
25
25
 
26
- {% block content_full_width_page %}
27
- {% include "utilities/obj_table.html" with table=vrf_table table_template="panel_table.html" heading="VRFs" disable_pagination=False %}
28
- {% include "utilities/obj_table.html" with table=prefix_table table_template="panel_table.html" heading="Prefixes" disable_pagination=False %}
29
- {% endblock content_full_width_page %}
26
+ {% block extra_nav_tabs %}
27
+ {% if perms.ipam.view_vrf %}
28
+ <li role="presentation"{% if active_tab == 'vrfs' %} class="active"{% endif %}>
29
+ <a href="{% url 'ipam:namespace_vrfs' pk=object.pk %}">VRFs <span class="badge">{{ vrf_count }}</span></a>
30
+ </li>
31
+ {% endif %}
32
+ {% if perms.ipam.view_prefix %}
33
+ <li role="presentation"{% if active_tab == 'prefixes' %} class="active"{% endif %}>
34
+ <a href="{% url 'ipam:namespace_prefixes' pk=object.pk %}">Prefixes <span class="badge">{{ prefix_count }}</span></a>
35
+ </li>
36
+ {% endif %}
37
+ {% if perms.ipam.view_ipaddress %}
38
+ <li role="presentation"{% if active_tab == 'ip-addresses' %} class="active"{% endif %}>
39
+ <a href="{% url 'ipam:namespace_ipaddresses' pk=object.pk %}">IP Addresses <span class="badge">{{ ip_address_count }}</span></a>
40
+ </li>
41
+ {% endif %}
42
+ {% endblock extra_nav_tabs %}
@@ -0,0 +1,11 @@
1
+ {% extends 'ipam/namespace_retrieve.html' %}
2
+
3
+ {% block title %}{{ block.super }} - VRFs{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="row">
7
+ <div class="col-md-12">
8
+ {% include 'utilities/obj_table.html' with table=vrf_table table_template='panel_table.html' heading='VRFs' bulk_edit_url='ipam:vrf_bulk_edit' bulk_delete_url='ipam:vrf_bulk_delete' %}
9
+ </div>
10
+ </div>
11
+ {% endblock %}
@@ -42,7 +42,7 @@
42
42
  </li>
43
43
  {% if perms.ipam.view_ipaddress %}
44
44
  <li role="presentation"{% if active_tab == 'ip-addresses' %} class="active"{% endif %}>
45
- <a href="{% url 'ipam:prefix_ipaddresses' pk=object.pk %}">IP Addresses <span class="badge">{{ object.get_child_ips.count }}</span></a>
45
+ <a href="{% url 'ipam:prefix_ipaddresses' pk=object.pk %}">IP Addresses <span class="badge">{{ object.ip_addresses.count }}</span></a>
46
46
  </li>
47
47
  {% endif %}
48
48
  {% endblock extra_nav_tabs %}
@@ -3,19 +3,6 @@
3
3
  {% load helpers %}
4
4
  {% load plugins %}
5
5
 
6
- {% block buttons %}
7
- {% plugin_buttons object %}
8
- {% if perms.ipam.add_vlangroup %}
9
- {% clone_button object %}
10
- {% endif %}
11
- {% if perms.ipam.change_vlangroup %}
12
- {% edit_button object key="pk" %}
13
- {% endif %}
14
- {% if perms.ipam.delete_vlangroup %}
15
- {% delete_button object key="pk" %}
16
- {% endif %}
17
- {% endblock buttons %}
18
-
19
6
  {% block content_left_page %}
20
7
  <div class="panel panel-default">
21
8
  <div class="panel-heading">
@@ -17,6 +17,12 @@
17
17
  {% render_field form.devices %}
18
18
  </div>
19
19
  </div>
20
+ <div class="panel panel-default">
21
+ <div class="panel-heading"><strong>Virtual Machines</strong></div>
22
+ <div class="panel-body">
23
+ {% render_field form.virtual_machines %}
24
+ </div>
25
+ </div>
20
26
  <div class="panel panel-default">
21
27
  <div class="panel-heading"><strong>Prefixes</strong></div>
22
28
  <div class="panel-body">
@@ -1,7 +1,5 @@
1
1
  import netaddr
2
2
 
3
- from django.test import override_settings
4
-
5
3
  from nautobot.core.testing.integration import SeleniumTestCase
6
4
  from nautobot.extras.models import Status
7
5
  from nautobot.ipam.models import Prefix
@@ -9,9 +7,7 @@ from nautobot.ipam.models import Prefix
9
7
 
10
8
  class PrefixHierarchyTest(SeleniumTestCase):
11
9
  """
12
- This test case proves that the setting `DISABLE_PREFIX_LIST_HIERARCHY` causes the prefix list view to
13
- render a flat list when set to True, instead of calculating parent/child relationships, and that by
14
- default it is disabled.
10
+ This test case tests the indented Prefix hierarchy displayed in the Prefix list view.
15
11
  """
16
12
 
17
13
  def setUp(self):
@@ -35,7 +31,8 @@ class PrefixHierarchyTest(SeleniumTestCase):
35
31
 
36
32
  # Navigate to Prefix list view
37
33
  self.browser.visit(self.live_server_url)
38
- self.browser.links.find_by_partial_text("IPAM").click()
34
+ # find_by_partial_text finds both Inventory > Provider Networks as well as the desired Networks top-level menu.
35
+ self.browser.links.find_by_partial_text("Networks")[1].click()
39
36
  self.browser.links.find_by_partial_text("Prefixes").click()
40
37
 
41
38
  self.assertEqual(len(self.browser.find_by_tag("tr")[1].find_by_text("10.0.0.0/16")), 1) # 10.0.0.0/16 is first
@@ -43,23 +40,3 @@ class PrefixHierarchyTest(SeleniumTestCase):
43
40
  self.assertTrue(
44
41
  self.browser.find_by_tag("tr")[2].find_by_tag("i").first.has_class("mdi-circle-small")
45
42
  ) # 10.0.0.0/24 is indented via an <i> tag
46
-
47
- @override_settings(DISABLE_PREFIX_LIST_HIERARCHY=True)
48
- def test_child_relationship_flat(self):
49
- """
50
- Test that 10.0.0.0/24 is NOT shown under 10.0.0.0/16, so the table is a flat list
51
- """
52
- status = Status.objects.get_for_model(Prefix).first()
53
- Prefix(prefix=netaddr.IPNetwork("10.0.0.0/16"), status=status).validated_save()
54
- Prefix(prefix=netaddr.IPNetwork("10.0.0.0/24"), status=status).validated_save()
55
-
56
- # Navigate to Prefix list view
57
- self.browser.visit(self.live_server_url)
58
- self.browser.links.find_by_partial_text("IPAM").click()
59
- self.browser.links.find_by_partial_text("Prefixes").click()
60
-
61
- self.assertEqual(len(self.browser.find_by_tag("tr")[1].find_by_text("10.0.0.0/16")), 1) # 10.0.0.0/16 is first
62
- self.assertEqual(len(self.browser.find_by_tag("tr")[2].find_by_text("10.0.0.0/24")), 1) # 10.0.0.0/24 is second
63
- self.assertEqual(
64
- len(self.browser.find_by_tag("tr")[2].find_by_tag("i")), 0
65
- ) # 10.0.0.0/24 is *not* indented via an <i> tag