nautobot 2.2.9__py3-none-any.whl → 2.3.0__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.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (697) hide show
  1. nautobot/apps/forms.py +4 -0
  2. nautobot/apps/models.py +10 -1
  3. nautobot/circuits/__init__.py +0 -1
  4. nautobot/circuits/apps.py +1 -0
  5. nautobot/circuits/factory.py +15 -3
  6. nautobot/circuits/filters.py +13 -0
  7. nautobot/circuits/forms.py +13 -0
  8. nautobot/circuits/migrations/0021_alter_circuit_status_alter_circuittermination__path.py +32 -0
  9. nautobot/circuits/migrations/0022_circuittermination_cloud_network.py +25 -0
  10. nautobot/circuits/models.py +16 -3
  11. nautobot/circuits/tables.py +16 -2
  12. nautobot/circuits/templates/circuits/circuittermination_create.html +10 -2
  13. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +6 -0
  14. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -1
  15. nautobot/circuits/tests/test_api.py +7 -5
  16. nautobot/circuits/tests/test_filters.py +12 -5
  17. nautobot/circuits/tests/test_models.py +33 -2
  18. nautobot/circuits/views.py +2 -3
  19. nautobot/cloud/__init__.py +0 -0
  20. nautobot/cloud/api/__init__.py +0 -0
  21. nautobot/cloud/api/serializers.py +54 -0
  22. nautobot/cloud/api/urls.py +16 -0
  23. nautobot/cloud/api/views.py +48 -0
  24. nautobot/cloud/apps.py +13 -0
  25. nautobot/cloud/factory.py +113 -0
  26. nautobot/cloud/filters.py +187 -0
  27. nautobot/cloud/forms.py +339 -0
  28. nautobot/cloud/homepage.py +43 -0
  29. nautobot/cloud/migrations/0001_initial.py +304 -0
  30. nautobot/cloud/migrations/__init__.py +0 -0
  31. nautobot/cloud/models.py +246 -0
  32. nautobot/cloud/navigation.py +85 -0
  33. nautobot/cloud/tables.py +157 -0
  34. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
  35. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +122 -0
  36. nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
  37. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +111 -0
  38. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +69 -0
  39. nautobot/cloud/templates/cloud/cloudservice_update.html +25 -0
  40. nautobot/cloud/tests/__init__.py +0 -0
  41. nautobot/cloud/tests/test_api.py +248 -0
  42. nautobot/cloud/tests/test_filters.py +125 -0
  43. nautobot/cloud/tests/test_models.py +43 -0
  44. nautobot/cloud/tests/test_views.py +153 -0
  45. nautobot/cloud/urls.py +14 -0
  46. nautobot/cloud/views.py +181 -0
  47. nautobot/core/__init__.py +0 -3
  48. nautobot/core/api/metadata.py +1 -0
  49. nautobot/core/api/parsers.py +7 -1
  50. nautobot/core/api/urls.py +1 -0
  51. nautobot/core/api/utils.py +1 -0
  52. nautobot/core/api/views.py +4 -0
  53. nautobot/core/apps/__init__.py +6 -3
  54. nautobot/core/constants.py +8 -0
  55. nautobot/core/factory.py +32 -1
  56. nautobot/core/filters.py +95 -13
  57. nautobot/core/forms/fields.py +10 -4
  58. nautobot/core/forms/forms.py +11 -3
  59. nautobot/core/forms/widgets.py +18 -1
  60. nautobot/core/graphql/schema.py +26 -4
  61. nautobot/core/jobs/__init__.py +16 -2
  62. nautobot/core/jobs/cleanup.py +100 -0
  63. nautobot/core/jobs/groups.py +38 -0
  64. nautobot/core/management/commands/generate_test_data.py +116 -3
  65. nautobot/core/models/__init__.py +34 -9
  66. nautobot/core/models/generics.py +19 -3
  67. nautobot/core/models/name_color_content_types.py +7 -28
  68. nautobot/core/models/querysets.py +4 -3
  69. nautobot/core/models/tree_queries.py +1 -1
  70. nautobot/core/models/utils.py +21 -5
  71. nautobot/core/settings.py +2 -17
  72. nautobot/core/settings.yaml +34 -13
  73. nautobot/core/settings_funcs.py +103 -0
  74. nautobot/core/tables.py +130 -56
  75. nautobot/core/templates/admin/search_form.html +1 -1
  76. nautobot/core/templates/buttons/add.html +11 -3
  77. nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
  78. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
  79. nautobot/core/templates/buttons/export.html +101 -53
  80. nautobot/core/templates/buttons/job_import.html +11 -3
  81. nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
  82. nautobot/core/templates/generic/object_bulk_update.html +3 -1
  83. nautobot/core/templates/generic/object_changelog.html +0 -9
  84. nautobot/core/templates/generic/object_list.html +156 -17
  85. nautobot/core/templates/generic/object_retrieve.html +80 -16
  86. nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
  87. nautobot/core/templates/inc/javascript.html +2 -0
  88. nautobot/core/templates/inc/media.html +2 -2
  89. nautobot/core/templates/inc/nav_menu.html +1 -0
  90. nautobot/core/templates/inc/paginator.html +7 -7
  91. nautobot/core/templates/inc/search_panel.html +2 -2
  92. nautobot/core/templates/inc/table.html +2 -2
  93. nautobot/core/templates/nautobot_config.py.j2 +13 -8
  94. nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
  95. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
  96. nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
  97. nautobot/core/templates/utilities/theme_preview.html +25 -8
  98. nautobot/core/templates/utilities/worker_status.html +152 -0
  99. nautobot/core/templatetags/buttons.py +335 -38
  100. nautobot/core/templatetags/form_helpers.py +1 -1
  101. nautobot/core/templatetags/helpers.py +181 -11
  102. nautobot/core/testing/api.py +5 -4
  103. nautobot/core/testing/filters.py +63 -14
  104. nautobot/core/testing/mixins.py +46 -0
  105. nautobot/core/testing/models.py +22 -0
  106. nautobot/core/testing/schema.py +4 -8
  107. nautobot/core/testing/views.py +31 -14
  108. nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
  109. nautobot/core/tests/integration/test_swagger.py +1 -1
  110. nautobot/core/tests/nautobot_config.py +0 -1
  111. nautobot/core/tests/runner.py +2 -2
  112. nautobot/core/tests/test_api.py +1 -0
  113. nautobot/core/tests/test_authentication.py +7 -2
  114. nautobot/core/tests/test_filters.py +11 -9
  115. nautobot/core/tests/test_forms.py +9 -0
  116. nautobot/core/tests/test_graphql.py +27 -16
  117. nautobot/core/tests/test_jobs.py +122 -0
  118. nautobot/core/tests/test_tables.py +3 -1
  119. nautobot/core/tests/test_templatetags_helpers.py +12 -5
  120. nautobot/core/tests/test_utils.py +31 -20
  121. nautobot/core/tests/test_views.py +6 -6
  122. nautobot/core/urls.py +8 -3
  123. nautobot/core/utils/deprecation.py +29 -0
  124. nautobot/core/utils/filtering.py +12 -9
  125. nautobot/core/utils/lookup.py +37 -2
  126. nautobot/core/utils/requests.py +4 -1
  127. nautobot/core/views/__init__.py +137 -24
  128. nautobot/core/views/generic.py +119 -67
  129. nautobot/core/views/mixins.py +105 -36
  130. nautobot/core/views/paginator.py +9 -3
  131. nautobot/core/views/renderers.py +121 -56
  132. nautobot/core/views/utils.py +81 -1
  133. nautobot/dcim/__init__.py +0 -1
  134. nautobot/dcim/api/serializers.py +180 -44
  135. nautobot/dcim/api/urls.py +7 -3
  136. nautobot/dcim/api/views.py +53 -7
  137. nautobot/dcim/apps.py +3 -0
  138. nautobot/dcim/choices.py +25 -0
  139. nautobot/dcim/constants.py +7 -0
  140. nautobot/dcim/factory.py +252 -18
  141. nautobot/dcim/filters/__init__.py +373 -193
  142. nautobot/dcim/filters/mixins.py +274 -1
  143. nautobot/dcim/forms.py +834 -121
  144. nautobot/dcim/graphql/types.py +2 -2
  145. nautobot/dcim/homepage.py +1 -1
  146. nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
  147. nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
  148. nautobot/dcim/migrations/0061_module_models.py +862 -0
  149. nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
  150. nautobot/dcim/models/__init__.py +8 -0
  151. nautobot/dcim/models/cables.py +15 -0
  152. nautobot/dcim/models/device_component_templates.py +207 -53
  153. nautobot/dcim/models/device_components.py +275 -99
  154. nautobot/dcim/models/devices.py +468 -13
  155. nautobot/dcim/models/racks.py +0 -1
  156. nautobot/dcim/navigation.py +47 -0
  157. nautobot/dcim/signals.py +3 -3
  158. nautobot/dcim/tables/__init__.py +35 -23
  159. nautobot/dcim/tables/devices.py +229 -43
  160. nautobot/dcim/tables/devicetypes.py +65 -9
  161. nautobot/dcim/tables/racks.py +5 -1
  162. nautobot/dcim/tables/template_code.py +46 -26
  163. nautobot/dcim/templates/dcim/cable_connect.html +76 -3
  164. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
  165. nautobot/dcim/templates/dcim/device/base.html +14 -6
  166. nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
  167. nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
  168. nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
  169. nautobot/dcim/templates/dcim/device/frontports.html +2 -3
  170. nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
  171. nautobot/dcim/templates/dcim/device/inventory.html +2 -3
  172. nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
  173. nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
  174. nautobot/dcim/templates/dcim/device/powerports.html +2 -3
  175. nautobot/dcim/templates/dcim/device/rearports.html +2 -3
  176. nautobot/dcim/templates/dcim/device.html +45 -1
  177. nautobot/dcim/templates/dcim/device_component.html +13 -5
  178. nautobot/dcim/templates/dcim/device_list.html +2 -1
  179. nautobot/dcim/templates/dcim/devicetype.html +99 -98
  180. nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
  181. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  182. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
  183. nautobot/dcim/templates/dcim/interface.html +17 -2
  184. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
  185. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  186. nautobot/dcim/templates/dcim/manufacturer.html +24 -0
  187. nautobot/dcim/templates/dcim/module/base.html +97 -0
  188. nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
  189. nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
  190. nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
  191. nautobot/dcim/templates/dcim/module_destroy.html +5 -0
  192. nautobot/dcim/templates/dcim/module_frontports.html +53 -0
  193. nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
  194. nautobot/dcim/templates/dcim/module_list.html +20 -0
  195. nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
  196. nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
  197. nautobot/dcim/templates/dcim/module_powerports.html +53 -0
  198. nautobot/dcim/templates/dcim/module_rearports.html +53 -0
  199. nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
  200. nautobot/dcim/templates/dcim/module_update.html +71 -0
  201. nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
  202. nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
  203. nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
  204. nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
  205. nautobot/dcim/templates/dcim/moduletype_retrieve.html +159 -0
  206. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
  207. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
  208. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  209. nautobot/dcim/tests/test_api.py +693 -208
  210. nautobot/dcim/tests/test_filters.py +843 -217
  211. nautobot/dcim/tests/test_models.py +1072 -8
  212. nautobot/dcim/tests/test_views.py +1510 -341
  213. nautobot/dcim/urls.py +17 -2
  214. nautobot/dcim/utils.py +2 -3
  215. nautobot/dcim/views.py +1106 -116
  216. nautobot/extras/__init__.py +0 -1
  217. nautobot/extras/api/serializers.py +115 -3
  218. nautobot/extras/api/urls.py +12 -0
  219. nautobot/extras/api/views.py +66 -0
  220. nautobot/extras/apps.py +2 -2
  221. nautobot/extras/choices.py +43 -0
  222. nautobot/extras/context_managers.py +13 -8
  223. nautobot/extras/datasources/git.py +2 -0
  224. nautobot/extras/factory.py +460 -9
  225. nautobot/extras/filters/__init__.py +174 -3
  226. nautobot/extras/filters/mixins.py +46 -43
  227. nautobot/extras/forms/base.py +24 -5
  228. nautobot/extras/forms/forms.py +227 -8
  229. nautobot/extras/forms/mixins.py +93 -0
  230. nautobot/extras/graphql/types.py +23 -10
  231. nautobot/extras/homepage.py +14 -1
  232. nautobot/extras/management/__init__.py +1 -0
  233. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
  234. nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
  235. nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
  236. nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
  237. nautobot/extras/migrations/0111_metadata.py +162 -0
  238. nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
  239. nautobot/extras/migrations/0113_saved_views.py +77 -0
  240. nautobot/extras/models/__init__.py +15 -1
  241. nautobot/extras/models/change_logging.py +3 -3
  242. nautobot/extras/models/contacts.py +4 -0
  243. nautobot/extras/models/customfields.py +18 -3
  244. nautobot/extras/models/groups.py +389 -225
  245. nautobot/extras/models/jobs.py +6 -3
  246. nautobot/extras/models/metadata.py +441 -0
  247. nautobot/extras/models/mixins.py +72 -62
  248. nautobot/extras/models/models.py +118 -9
  249. nautobot/extras/models/relationships.py +9 -2
  250. nautobot/extras/models/tags.py +13 -2
  251. nautobot/extras/navigation.py +57 -0
  252. nautobot/extras/plugins/__init__.py +3 -1
  253. nautobot/extras/querysets.py +30 -66
  254. nautobot/extras/signals.py +95 -100
  255. nautobot/extras/tables.py +165 -12
  256. nautobot/extras/templates/extras/dynamicgroup.html +44 -15
  257. nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
  258. nautobot/extras/templates/extras/job.html +1 -1
  259. nautobot/extras/templates/extras/jobresult.html +61 -74
  260. nautobot/extras/templates/extras/metadatatype_create.html +89 -0
  261. nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
  262. nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
  263. nautobot/extras/templates/extras/objectchange_list.html +0 -12
  264. nautobot/extras/templates/extras/plugins_list.html +1 -3
  265. nautobot/extras/templates/extras/role_retrieve.html +48 -0
  266. nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
  267. nautobot/extras/tests/integration/test_customfields.py +1 -0
  268. nautobot/extras/tests/test_api.py +509 -23
  269. nautobot/extras/tests/test_changelog.py +20 -9
  270. nautobot/extras/tests/test_context_managers.py +22 -15
  271. nautobot/extras/tests/test_datasources.py +13 -1
  272. nautobot/extras/tests/test_dynamicgroups.py +201 -171
  273. nautobot/extras/tests/test_filters.py +211 -12
  274. nautobot/extras/tests/test_jobs.py +6 -6
  275. nautobot/extras/tests/test_models.py +501 -4
  276. nautobot/extras/tests/test_relationships.py +1 -0
  277. nautobot/extras/tests/test_views.py +565 -8
  278. nautobot/extras/tests/test_webhooks.py +1 -1
  279. nautobot/extras/urls.py +5 -0
  280. nautobot/extras/utils.py +51 -11
  281. nautobot/extras/views.py +542 -76
  282. nautobot/ipam/__init__.py +0 -1
  283. nautobot/ipam/apps.py +1 -0
  284. nautobot/ipam/factory.py +17 -19
  285. nautobot/ipam/filters.py +13 -0
  286. nautobot/ipam/forms.py +8 -4
  287. nautobot/ipam/graphql/types.py +2 -2
  288. nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
  289. nautobot/ipam/models.py +11 -8
  290. nautobot/ipam/querysets.py +1 -1
  291. nautobot/ipam/signals.py +4 -2
  292. nautobot/ipam/tables.py +5 -0
  293. nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
  294. nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
  295. nautobot/ipam/templates/ipam/prefix.html +1 -0
  296. nautobot/ipam/tests/test_api.py +37 -18
  297. nautobot/ipam/tests/test_filters.py +26 -2
  298. nautobot/ipam/tests/test_models.py +6 -0
  299. nautobot/ipam/tests/test_querysets.py +1 -1
  300. nautobot/ipam/tests/test_views.py +3 -2
  301. nautobot/ipam/urls.py +2 -2
  302. nautobot/ipam/views.py +18 -26
  303. nautobot/project-static/css/base.css +20 -0
  304. nautobot/project-static/css/dark.css +11 -0
  305. nautobot/project-static/docs/404.html +892 -88
  306. nautobot/project-static/docs/apps/index.html +892 -88
  307. nautobot/project-static/docs/apps/nautobot-apps.html +892 -88
  308. nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
  309. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +1 -0
  310. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
  311. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +919 -120
  312. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +904 -101
  313. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1618 -903
  314. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +935 -144
  315. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +977 -188
  316. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +901 -99
  317. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +897 -93
  318. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +991 -193
  319. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +974 -131
  320. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1078 -272
  321. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1242 -334
  322. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1727 -875
  323. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1164 -381
  324. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2088 -1374
  325. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2246 -1422
  326. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +912 -111
  327. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +963 -163
  328. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1010 -223
  329. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1913 -1277
  330. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1846 -1102
  331. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +904 -101
  332. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2331 -1699
  333. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1802 -1024
  334. nautobot/project-static/docs/development/apps/api/configuration-view.html +892 -88
  335. nautobot/project-static/docs/development/apps/api/database-backend-config.html +892 -88
  336. nautobot/project-static/docs/development/apps/api/models/django-admin.html +892 -88
  337. nautobot/project-static/docs/development/apps/api/models/global-search.html +892 -88
  338. nautobot/project-static/docs/development/apps/api/models/graphql.html +892 -88
  339. nautobot/project-static/docs/development/apps/api/models/index.html +942 -90
  340. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +892 -88
  341. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +892 -88
  342. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +892 -88
  343. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +892 -88
  344. nautobot/project-static/docs/development/apps/api/platform-features/index.html +892 -88
  345. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +892 -88
  346. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +892 -88
  347. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +892 -88
  348. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +892 -88
  349. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +892 -88
  350. nautobot/project-static/docs/development/apps/api/prometheus.html +892 -88
  351. nautobot/project-static/docs/development/apps/api/setup.html +892 -88
  352. nautobot/project-static/docs/development/apps/api/testing.html +892 -88
  353. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +892 -88
  354. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +892 -88
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +892 -88
  356. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +892 -88
  357. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +892 -88
  358. nautobot/project-static/docs/development/apps/api/views/base-template.html +892 -88
  359. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +892 -88
  360. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +892 -88
  361. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +892 -88
  362. nautobot/project-static/docs/development/apps/api/views/index.html +892 -88
  363. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +892 -88
  364. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +892 -88
  365. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +892 -88
  366. nautobot/project-static/docs/development/apps/api/views/notes.html +892 -88
  367. nautobot/project-static/docs/development/apps/api/views/rest-api.html +892 -88
  368. nautobot/project-static/docs/development/apps/api/views/urls.html +892 -88
  369. nautobot/project-static/docs/development/apps/index.html +892 -88
  370. nautobot/project-static/docs/development/apps/migration/code-updates.html +892 -88
  371. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +892 -88
  372. nautobot/project-static/docs/development/apps/migration/from-v1.html +892 -88
  373. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +892 -88
  374. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +892 -88
  375. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +892 -88
  376. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +892 -88
  377. nautobot/project-static/docs/development/apps/porting-from-netbox.html +892 -88
  378. nautobot/project-static/docs/development/core/application-registry.html +892 -88
  379. nautobot/project-static/docs/development/core/best-practices.html +893 -88
  380. nautobot/project-static/docs/development/core/bootstrap-ui.html +892 -88
  381. nautobot/project-static/docs/development/core/caching.html +892 -88
  382. nautobot/project-static/docs/development/core/controllers.html +892 -88
  383. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +892 -88
  384. nautobot/project-static/docs/development/core/generic-views.html +892 -88
  385. nautobot/project-static/docs/development/core/getting-started.html +892 -88
  386. nautobot/project-static/docs/development/core/homepage.html +892 -88
  387. nautobot/project-static/docs/development/core/index.html +892 -88
  388. nautobot/project-static/docs/development/core/model-checklist.html +901 -89
  389. nautobot/project-static/docs/development/core/model-features.html +892 -88
  390. nautobot/project-static/docs/development/core/natural-keys.html +892 -88
  391. nautobot/project-static/docs/development/core/navigation-menu.html +892 -88
  392. nautobot/project-static/docs/development/core/release-checklist.html +895 -91
  393. nautobot/project-static/docs/development/core/role-internals.html +892 -88
  394. nautobot/project-static/docs/development/core/settings.html +892 -88
  395. nautobot/project-static/docs/development/core/style-guide.html +893 -89
  396. nautobot/project-static/docs/development/core/templates.html +904 -89
  397. nautobot/project-static/docs/development/core/testing.html +892 -88
  398. nautobot/project-static/docs/development/core/user-preferences.html +892 -88
  399. nautobot/project-static/docs/development/index.html +892 -88
  400. nautobot/project-static/docs/development/jobs/index.html +893 -89
  401. nautobot/project-static/docs/development/jobs/migration/from-v1.html +892 -88
  402. nautobot/project-static/docs/index.html +892 -88
  403. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
  404. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
  405. nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
  406. nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
  407. nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
  408. nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
  409. nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
  410. nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
  411. nautobot/project-static/docs/models/dcim/module.html +15 -0
  412. nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
  413. nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
  414. nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
  415. nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
  416. nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
  417. nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
  418. nautobot/project-static/docs/models/extras/role.html +15 -0
  419. nautobot/project-static/docs/models/extras/savedview.html +15 -0
  420. nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
  421. nautobot/project-static/docs/models/extras/status.html +15 -0
  422. nautobot/project-static/docs/objects.inv +0 -0
  423. nautobot/project-static/docs/overview/application_stack.html +900 -89
  424. nautobot/project-static/docs/overview/design_philosophy.html +892 -88
  425. nautobot/project-static/docs/release-notes/index.html +1129 -92
  426. nautobot/project-static/docs/release-notes/version-1.0.html +892 -88
  427. nautobot/project-static/docs/release-notes/version-1.1.html +892 -88
  428. nautobot/project-static/docs/release-notes/version-1.2.html +892 -88
  429. nautobot/project-static/docs/release-notes/version-1.3.html +892 -88
  430. nautobot/project-static/docs/release-notes/version-1.4.html +892 -88
  431. nautobot/project-static/docs/release-notes/version-1.5.html +893 -89
  432. nautobot/project-static/docs/release-notes/version-1.6.html +893 -89
  433. nautobot/project-static/docs/release-notes/version-2.0.html +892 -88
  434. nautobot/project-static/docs/release-notes/version-2.1.html +892 -88
  435. nautobot/project-static/docs/release-notes/version-2.2.html +895 -91
  436. nautobot/project-static/docs/release-notes/version-2.3.html +9954 -0
  437. nautobot/project-static/docs/requirements.txt +5 -5
  438. nautobot/project-static/docs/search/search_index.json +1 -1
  439. nautobot/project-static/docs/sitemap.xml +331 -256
  440. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  441. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +892 -88
  442. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +892 -88
  443. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +892 -88
  444. nautobot/project-static/docs/user-guide/administration/configuration/index.html +892 -88
  445. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +992 -174
  446. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +892 -88
  447. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +892 -88
  448. nautobot/project-static/docs/user-guide/administration/guides/caching.html +892 -88
  449. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +896 -88
  450. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +892 -88
  451. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +892 -88
  452. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +892 -88
  453. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +892 -88
  454. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +892 -88
  455. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +892 -88
  456. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +892 -88
  457. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +892 -88
  458. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +944 -153
  459. nautobot/project-static/docs/user-guide/administration/installation/index.html +901 -93
  460. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +934 -122
  461. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +954 -157
  462. nautobot/project-static/docs/user-guide/administration/installation/services.html +913 -112
  463. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +908 -99
  464. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +892 -88
  465. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +892 -88
  466. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +892 -88
  467. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +892 -88
  468. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +893 -89
  469. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +892 -88
  470. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +892 -88
  471. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +892 -88
  472. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +892 -88
  473. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +892 -88
  474. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +892 -88
  475. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +892 -88
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +892 -88
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +892 -88
  478. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +892 -88
  479. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +892 -88
  480. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +893 -89
  481. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +892 -88
  482. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +896 -88
  483. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +895 -91
  484. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
  485. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
  486. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
  487. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
  488. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
  489. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
  491. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +906 -102
  492. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +923 -105
  493. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +923 -105
  494. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +918 -100
  495. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +923 -105
  496. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +906 -102
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +906 -102
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +913 -105
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +920 -116
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +921 -117
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +918 -114
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +906 -102
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +914 -105
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +926 -108
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +936 -118
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +928 -106
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +906 -102
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +937 -119
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +928 -110
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +918 -114
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +921 -117
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +923 -115
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +916 -112
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +906 -102
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +940 -83
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +924 -106
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +906 -102
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +943 -86
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +921 -103
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +929 -125
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +918 -114
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +906 -102
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +922 -104
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +924 -106
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +906 -102
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +906 -102
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +906 -102
  532. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +936 -88
  533. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +892 -88
  534. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +897 -89
  535. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +897 -89
  536. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +892 -88
  537. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +892 -88
  538. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +892 -88
  539. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +892 -88
  540. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +892 -88
  541. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +892 -88
  542. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +892 -88
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +892 -88
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +892 -88
  545. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +892 -88
  546. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +901 -96
  547. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +892 -88
  548. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +892 -88
  549. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +892 -88
  550. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +892 -88
  551. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +892 -88
  552. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +897 -89
  553. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +892 -88
  554. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +892 -88
  555. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +892 -88
  556. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +892 -88
  557. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +892 -88
  558. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +892 -88
  559. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +892 -88
  560. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +892 -88
  561. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +892 -88
  562. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +892 -88
  563. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +892 -88
  564. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +892 -88
  565. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +892 -88
  566. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
  567. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
  568. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
  569. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
  570. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
  571. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
  572. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
  573. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
  574. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
  575. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
  576. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
  587. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
  589. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
  590. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
  591. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +892 -88
  592. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +892 -88
  593. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +892 -88
  594. nautobot/project-static/docs/user-guide/index.html +892 -88
  595. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +892 -88
  596. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +892 -88
  597. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +892 -88
  598. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +892 -88
  599. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1258 -785
  600. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +895 -91
  601. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +892 -88
  602. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +892 -88
  603. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +892 -88
  604. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +892 -88
  605. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +892 -88
  606. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +892 -88
  607. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +892 -88
  608. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +892 -88
  609. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +892 -88
  610. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +896 -88
  611. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +892 -88
  612. nautobot/project-static/docs/user-guide/platform-functionality/note.html +895 -91
  613. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +9061 -0
  614. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +895 -91
  615. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +892 -88
  616. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +892 -88
  617. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +892 -88
  618. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +892 -88
  619. nautobot/project-static/docs/user-guide/platform-functionality/role.html +895 -91
  620. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
  621. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +895 -91
  622. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
  623. nautobot/project-static/docs/user-guide/platform-functionality/status.html +892 -88
  624. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +892 -88
  625. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +950 -121
  626. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +892 -88
  627. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +892 -88
  628. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +892 -88
  629. nautobot/project-static/js/forms.js +71 -0
  630. nautobot/project-static/js/table_sorting_indicator.js +46 -0
  631. nautobot/project-static/js/tableconfig.js +6 -1
  632. nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
  633. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
  634. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
  635. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
  636. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
  637. nautobot/tenancy/__init__.py +0 -1
  638. nautobot/tenancy/apps.py +1 -0
  639. nautobot/tenancy/factory.py +3 -2
  640. nautobot/tenancy/filters/__init__.py +1 -0
  641. nautobot/tenancy/forms.py +1 -1
  642. nautobot/tenancy/templates/tenancy/tenant.html +24 -20
  643. nautobot/tenancy/views.py +11 -10
  644. nautobot/users/__init__.py +0 -1
  645. nautobot/users/api/serializers.py +1 -1
  646. nautobot/users/api/views.py +4 -2
  647. nautobot/users/apps.py +3 -2
  648. nautobot/users/factory.py +3 -3
  649. nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
  650. nautobot/users/models.py +12 -0
  651. nautobot/users/tests/test_filters.py +6 -3
  652. nautobot/users/urls.py +8 -0
  653. nautobot/virtualization/__init__.py +0 -1
  654. nautobot/virtualization/apps.py +1 -0
  655. nautobot/virtualization/filters.py +6 -1
  656. nautobot/virtualization/forms.py +11 -3
  657. nautobot/virtualization/graphql/types.py +2 -2
  658. nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
  659. nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
  660. nautobot/virtualization/models.py +0 -2
  661. nautobot/virtualization/tables.py +10 -3
  662. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  663. nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
  664. nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
  665. nautobot/virtualization/tests/test_api.py +9 -4
  666. nautobot/virtualization/tests/test_filters.py +22 -0
  667. nautobot/virtualization/tests/test_models.py +7 -3
  668. nautobot/virtualization/tests/test_views.py +19 -3
  669. nautobot/virtualization/urls.py +2 -2
  670. nautobot/virtualization/views.py +10 -32
  671. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/METADATA +20 -18
  672. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/RECORD +677 -557
  673. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +0 -1
  674. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +0 -1
  675. nautobot/project-static/materialdesignicons-6.5.95/.github/ISSUE_TEMPLATE.md +0 -3
  676. nautobot/project-static/materialdesignicons-6.5.95/README.md +0 -25
  677. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css +0 -26654
  678. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css.map +0 -16
  679. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css +0 -3
  680. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css.map +0 -16
  681. nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff +0 -0
  682. nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff2 +0 -0
  683. nautobot/project-static/materialdesignicons-6.5.95/package.json +0 -28
  684. nautobot/project-static/materialdesignicons-6.5.95/preview.html +0 -717
  685. nautobot/project-static/materialdesignicons-6.5.95/scss/_animated.scss +0 -27
  686. nautobot/project-static/materialdesignicons-6.5.95/scss/_core.scss +0 -10
  687. nautobot/project-static/materialdesignicons-6.5.95/scss/_extras.scss +0 -65
  688. nautobot/project-static/materialdesignicons-6.5.95/scss/_functions.scss +0 -20
  689. nautobot/project-static/materialdesignicons-6.5.95/scss/_icons.scss +0 -10
  690. nautobot/project-static/materialdesignicons-6.5.95/scss/_path.scss +0 -10
  691. nautobot/project-static/materialdesignicons-6.5.95/scss/_variables.scss +0 -6606
  692. nautobot/project-static/materialdesignicons-6.5.95/scss/materialdesignicons.scss +0 -8
  693. /nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/LICENSE +0 -0
  694. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/LICENSE.txt +0 -0
  695. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/NOTICE +0 -0
  696. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/WHEEL +0 -0
  697. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/entry_points.txt +0 -0
@@ -7,9 +7,11 @@ from nautobot.tenancy.tables import TenantGroupTable
7
7
 
8
8
 
9
9
  class TableTestCase(TestCase):
10
+ maxDiff = None
11
+
10
12
  def _validate_sorted_tree_queryset_same_with_table_queryset(self, queryset, table_class, field_name):
11
13
  with self.subTest(f"Assert sorting {table_class.__name__} on '{field_name}'"):
12
- table = table_class(queryset, order_by=field_name)
14
+ table = table_class(queryset.with_tree_fields(), order_by=field_name)
13
15
  table_queryset_data = table.data.data.values_list("pk", flat=True)
14
16
  sorted_queryset = queryset.with_tree_fields().extra(order_by=[field_name]).values_list("pk", flat=True)
15
17
  self.assertEqual(list(table_queryset_data), list(sorted_queryset))
@@ -22,14 +22,14 @@ class NautobotTemplatetagsHelperTest(TestCase):
22
22
  location.description = ""
23
23
  location.save()
24
24
  self.assertEqual(
25
- helpers.hyperlinked_object(location), f'<a href="/dcim/locations/{location.pk}/">{location.name}</a>'
25
+ helpers.hyperlinked_object(location), f'<a href="/dcim/locations/{location.pk}/">{location.display}</a>'
26
26
  )
27
27
  # An object with get_absolute_url and a description gives a titled hyperlink
28
28
  location.description = "An important location"
29
29
  location.save()
30
30
  self.assertEqual(
31
31
  helpers.hyperlinked_object(location),
32
- f'<a href="/dcim/locations/{location.pk}/" title="An important location">{location.name}</a>',
32
+ f'<a href="/dcim/locations/{location.pk}/" title="An important location">{location.display}</a>',
33
33
  )
34
34
  # Optionally you can request a field other than the object's display string
35
35
  self.assertEqual(
@@ -114,6 +114,13 @@ class NautobotTemplatetagsHelperTest(TestCase):
114
114
  )
115
115
  self.assertEqual('"I am UTF-8! 😀"', helpers.render_json("I am UTF-8! 😀", False))
116
116
 
117
+ def test_render_uptime(self):
118
+ self.assertEqual(helpers.render_uptime(1024768), "11 days 20 hours 39 minutes")
119
+ self.assertEqual(helpers.render_uptime(""), helpers.placeholder(""))
120
+ self.assertEqual(helpers.render_uptime("123456"), "1 day 10 hours 17 minutes")
121
+ self.assertEqual(helpers.render_uptime(0), "0 days 0 hours 0 minutes")
122
+ self.assertEqual(helpers.render_uptime("foo bar"), helpers.placeholder("foo bar"))
123
+
117
124
  def test_render_yaml(self):
118
125
  self.assertEqual(
119
126
  helpers.render_yaml({"syntax": "highlight"}), '<code class="language-yaml">syntax: highlight\n</code>'
@@ -251,7 +258,7 @@ class NautobotTemplatetagsHelperTest(TestCase):
251
258
  f'<span class="label" style="color: {fbcolor}; background-color: #{color}">{display}</span>',
252
259
  )
253
260
  # Assert when obj is None
254
- self.assertEqual(helpers.hyperlinked_object_with_color(obj=None), "")
261
+ self.assertEqual(helpers.hyperlinked_object_with_color(obj=None), '<span class="text-muted">&mdash;</span>')
255
262
 
256
263
  @override_settings(BANNER_TOP="¡Hola, mundo!")
257
264
  @override_config(example_app__SAMPLE_VARIABLE="Testing")
@@ -296,14 +303,14 @@ class NautobotTemplatetagsHelperTest(TestCase):
296
303
  location.save()
297
304
  self.assertEqual(
298
305
  helpers.hyperlinked_object_target_new_tab(location),
299
- f'<a href="/dcim/locations/{location.pk}/" target="_blank" rel="noreferrer">{location.name}</a>',
306
+ f'<a href="/dcim/locations/{location.pk}/" target="_blank" rel="noreferrer">{location.display}</a>',
300
307
  )
301
308
  # An object with get_absolute_url and a description gives a titled hyperlink
302
309
  location.description = "An important location"
303
310
  location.save()
304
311
  self.assertEqual(
305
312
  helpers.hyperlinked_object_target_new_tab(location),
306
- f'<a href="/dcim/locations/{location.pk}/" title="An important location" target="_blank" rel="noreferrer">{location.name}</a>',
313
+ f'<a href="/dcim/locations/{location.pk}/" title="An important location" target="_blank" rel="noreferrer">{location.display}</a>',
307
314
  )
308
315
  # Optionally you can request a field other than the object's display string
309
316
  self.assertEqual(
@@ -7,12 +7,12 @@ from django.contrib.contenttypes.models import ContentType
7
7
  from django.core.exceptions import ValidationError
8
8
  from django.db.models import Q
9
9
  from django.http import QueryDict
10
- from django.test import TestCase
11
10
 
12
11
  from nautobot.circuits import models as circuits_models
13
12
  from nautobot.core import exceptions, forms, settings_funcs
14
13
  from nautobot.core.api import utils as api_utils
15
14
  from nautobot.core.models import fields as core_fields, utils as models_utils, validators
15
+ from nautobot.core.testing import TestCase
16
16
  from nautobot.core.utils import data as data_utils, filtering, lookup, requests
17
17
  from nautobot.core.utils.migrations import update_object_change_ct_for_replaced_models
18
18
  from nautobot.dcim import filters as dcim_filters, forms as dcim_forms, models as dcim_models, tables
@@ -485,61 +485,61 @@ class LookupRelatedFunctionTest(TestCase):
485
485
  location_fields = ["comments", "name", "contact_email", "physical_address", "shipping_address"]
486
486
  for field_name in location_fields:
487
487
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, field_name)
488
- self.assertIs(type(form_field), forms.MultiValueCharField)
488
+ self.assertIsInstance(form_field, forms.MultiValueCharField)
489
489
 
490
490
  device_fields = ["serial", "name"]
491
491
  for field_name in device_fields:
492
492
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, field_name)
493
- self.assertIs(type(form_field), forms.MultiValueCharField)
493
+ self.assertIsInstance(form_field, forms.MultiValueCharField)
494
494
 
495
495
  with self.subTest("Test IntegerField"):
496
496
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, "asn")
497
- self.assertIs(type(form_field), django_forms.IntegerField)
497
+ self.assertIsInstance(form_field, django_forms.IntegerField)
498
498
 
499
499
  device_fields = ["vc_position", "vc_priority"]
500
500
  for field_name in device_fields:
501
501
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, field_name)
502
- self.assertIs(type(form_field), django_forms.IntegerField)
502
+ self.assertIsInstance(form_field, django_forms.IntegerField)
503
503
 
504
504
  with self.subTest("Test DynamicModelMultipleChoiceField"):
505
505
  location_fields = ["tenant", "status"]
506
506
  for field_name in location_fields:
507
507
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, field_name)
508
- self.assertIs(type(form_field), forms.DynamicModelMultipleChoiceField)
508
+ self.assertIsInstance(form_field, forms.DynamicModelMultipleChoiceField)
509
509
 
510
510
  device_fields = ["cluster", "device_type", "location"]
511
511
  for field_name in device_fields:
512
512
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, field_name)
513
- self.assertIs(type(form_field), forms.DynamicModelMultipleChoiceField)
513
+ self.assertIsInstance(form_field, forms.DynamicModelMultipleChoiceField)
514
514
 
515
515
  location_fields = ["has_circuit_terminations", "has_devices"]
516
516
  for field_name in location_fields:
517
517
  with self.subTest("Test ChoiceField", model=dcim_models.Location, field_name=field_name):
518
518
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, field_name)
519
- self.assertIs(type(form_field), django_forms.ChoiceField)
520
- self.assertIs(type(form_field.widget), forms.StaticSelect2)
519
+ self.assertIsInstance(form_field, django_forms.ChoiceField)
520
+ self.assertIsInstance(form_field.widget, forms.StaticSelect2)
521
521
 
522
522
  device_fields = ["has_console_ports", "has_interfaces", "local_config_context_data"]
523
523
  for field_name in device_fields:
524
524
  with self.subTest("Test ChoiceField", model=dcim_models.Device, field_name=field_name):
525
525
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, field_name)
526
- self.assertIs(type(form_field), django_forms.ChoiceField)
527
- self.assertIs(type(form_field.widget), forms.StaticSelect2)
526
+ self.assertIsInstance(form_field, django_forms.ChoiceField)
527
+ self.assertIsInstance(form_field.widget, forms.StaticSelect2)
528
528
 
529
529
  with self.subTest("Test MultipleChoiceField"):
530
530
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, "face")
531
- self.assertIs(type(form_field), django_forms.MultipleChoiceField)
531
+ self.assertIsInstance(form_field, django_forms.MultipleChoiceField)
532
532
 
533
533
  with self.subTest("Test DateTimePicker"):
534
534
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, "last_updated")
535
- self.assertIs(type(form_field.widget), forms.DateTimePicker)
535
+ self.assertIsInstance(form_field.widget, forms.DateTimePicker)
536
536
 
537
537
  form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, "last_updated")
538
- self.assertIs(type(form_field.widget), forms.DateTimePicker)
538
+ self.assertIsInstance(form_field.widget, forms.DateTimePicker)
539
539
 
540
540
  with self.subTest("Test DatePicker"):
541
541
  form_field = filtering.get_filterset_parameter_form_field(circuits_models.Circuit, "install_date")
542
- self.assertIs(type(form_field.widget), forms.DatePicker)
542
+ self.assertIsInstance(form_field.widget, forms.DatePicker)
543
543
 
544
544
  with self.subTest("Test Invalid parameter"):
545
545
  with self.assertRaises(exceptions.FilterSetFieldNotFound) as err:
@@ -548,16 +548,27 @@ class LookupRelatedFunctionTest(TestCase):
548
548
 
549
549
  with self.subTest("Test Content types"):
550
550
  form_field = filtering.get_filterset_parameter_form_field(extras_models.Status, "content_types")
551
- self.assertIs(type(form_field), forms.MultipleContentTypeField)
551
+ self.assertIsInstance(form_field, forms.MultipleContentTypeField)
552
552
 
553
553
  # Assert total ContentTypes generated by form_field is == total `content_types` generated by TaggableClassesQuery
554
554
  form_field = filtering.get_filterset_parameter_form_field(extras_models.Tag, "content_types")
555
- self.assertIs(type(form_field), forms.MultipleContentTypeField)
556
- self.assertEqual(form_field.queryset.count(), extras_utils.TaggableClassesQuery().as_queryset().count())
555
+ self.assertIsInstance(form_field, forms.MultipleContentTypeField)
556
+ self.assertQuerysetEqualAndNotEmpty(form_field.queryset, extras_utils.TaggableClassesQuery().as_queryset())
557
557
 
558
558
  form_field = filtering.get_filterset_parameter_form_field(extras_models.JobHook, "content_types")
559
- self.assertIs(type(form_field), forms.MultipleContentTypeField)
560
- self.assertEqual(form_field.queryset.count(), extras_utils.ChangeLoggedModelsQuery().as_queryset().count())
559
+ self.assertIsInstance(form_field, forms.MultipleContentTypeField)
560
+ self.assertQuerysetEqualAndNotEmpty(
561
+ form_field.queryset, extras_utils.ChangeLoggedModelsQuery().as_queryset()
562
+ )
563
+
564
+ with self.subTest("Test prefers_id"):
565
+ form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, "location")
566
+ self.assertEqual("id", form_field.to_field_name)
567
+ form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, "vlans")
568
+ self.assertEqual("id", form_field.to_field_name)
569
+ # Test prefers_id=False (default)
570
+ form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, "racks")
571
+ self.assertEqual("name", form_field.to_field_name)
561
572
 
562
573
  def test_convert_querydict_to_factory_formset_dict(self):
563
574
  location_filter_set = dcim_filters.LocationFilterSet()
@@ -480,9 +480,9 @@ class ErrorPagesTestCase(TestCase):
480
480
  def test_500_default_support_message(self, mock_get):
481
481
  """Nautobot's custom 500 page should be used and should include a default support message."""
482
482
  url = reverse("home")
483
- with self.assertTemplateUsed("500.html"):
484
- self.client.raise_request_exception = False
485
- response = self.client.get(url)
483
+ self.client.raise_request_exception = False
484
+ response = self.client.get(url)
485
+ self.assertTemplateUsed(response, "500.html")
486
486
  self.assertContains(response, "Network to Code", status_code=500)
487
487
  response_content = response.content.decode(response.charset)
488
488
  self.assertInHTML(
@@ -497,9 +497,9 @@ class ErrorPagesTestCase(TestCase):
497
497
  def test_500_custom_support_message(self, mock_get):
498
498
  """Nautobot's custom 500 page should be used and should include a custom support message if defined."""
499
499
  url = reverse("home")
500
- with self.assertTemplateUsed("500.html"):
501
- self.client.raise_request_exception = False
502
- response = self.client.get(url)
500
+ self.client.raise_request_exception = False
501
+ response = self.client.get(url)
502
+ self.assertTemplateUsed(response, "500.html")
503
503
  self.assertNotContains(response, "Network to Code", status_code=500)
504
504
  response_content = response.content.decode(response.charset)
505
505
  self.assertInHTML("Hello world!", response_content)
nautobot/core/urls.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from django.conf import settings
2
- from django.conf.urls import include, url
3
- from django.urls import path
2
+ from django.urls import include, path
4
3
  from django.views.generic import TemplateView
5
4
  from django.views.static import serve
6
5
 
@@ -13,6 +12,7 @@ from nautobot.core.views import (
13
12
  SearchView,
14
13
  StaticMediaFailureView,
15
14
  ThemePreviewView,
15
+ WorkerStatusView,
16
16
  )
17
17
  from nautobot.extras.plugins.urls import (
18
18
  apps_patterns,
@@ -32,11 +32,14 @@ urlpatterns = [
32
32
  path("logout/", LogoutView.as_view(), name="logout"),
33
33
  # Apps
34
34
  path("circuits/", include("nautobot.circuits.urls")),
35
+ path("cloud/", include("nautobot.cloud.urls")),
35
36
  path("dcim/", include("nautobot.dcim.urls")),
36
37
  path("extras/", include("nautobot.extras.urls")),
37
38
  path("ipam/", include("nautobot.ipam.urls")),
38
39
  path("tenancy/", include("nautobot.tenancy.urls")),
40
+ # TODO: deprecate this url and use users
39
41
  path("user/", include("nautobot.users.urls")),
42
+ path("users/", include("nautobot.users.urls", "users")),
40
43
  path("virtualization/", include("nautobot.virtualization.urls")),
41
44
  # API
42
45
  path("api/", include("nautobot.core.api.urls")),
@@ -57,12 +60,14 @@ urlpatterns = [
57
60
  # django-health-check
58
61
  path(r"health/", include("health_check.urls")),
59
62
  # FileProxy attachments download/get URLs used in admin views only
60
- url(
63
+ path(
61
64
  "files/download/",
62
65
  get_file_with_authorization,
63
66
  {"add_attachment_headers": True},
64
67
  name="db_file_storage.download_file",
65
68
  ),
69
+ # Celery worker status page
70
+ path("worker-status/", WorkerStatusView.as_view(), name="worker_status"),
66
71
  # Templated css file
67
72
  path(
68
73
  "template.css", TemplateView.as_view(template_name="template.css", content_type="text/css"), name="template_css"
@@ -45,3 +45,32 @@ def class_deprecated(message):
45
45
  def class_deprecated_in_favor_of(replacement_class):
46
46
  """Decorator to mark a class as deprecated and suggest a replacement class if it is subclassed from."""
47
47
  return class_deprecated(f"please migrate your code to inherit from class {replacement_class.__name__} instead")
48
+
49
+
50
+ def method_deprecated(message):
51
+ """Decorator to mark a method as deprecated with a custom message about what to call instead."""
52
+
53
+ def decorate(method):
54
+ def decorated_method(*args, **kwargs):
55
+ warnings.warn(
56
+ f"Method `{method.__name__}` is deprecated, and will be removed in a future Nautobot release. "
57
+ f"{message}",
58
+ DeprecationWarning,
59
+ stacklevel=2,
60
+ )
61
+ if LOG_DEPRECATION_WARNINGS:
62
+ logger.warning(
63
+ f"Method `{method.__name__}` is deprecated, and will be removed in a future Nautobot release. "
64
+ f"{message}",
65
+ stacklevel=2,
66
+ )
67
+ return method(*args, **kwargs)
68
+
69
+ return decorated_method
70
+
71
+ return decorate
72
+
73
+
74
+ def method_deprecated_in_favor_of(replacement_method):
75
+ """Decorator to mark a method as deprecated and suggest a replacement method instead."""
76
+ return method_deprecated(f"Please migrate your code to call `{replacement_method.__name__}` instead.")
@@ -96,8 +96,8 @@ def get_filterset_parameter_form_field(model, parameter, filterset=None):
96
96
  StaticSelect2Multiple,
97
97
  )
98
98
  from nautobot.dcim.models import Device
99
- from nautobot.extras.filters import ContentTypeMultipleChoiceFilter, CustomFieldFilterMixin, StatusFilter
100
- from nautobot.extras.models import ConfigContext, Role, Status, Tag
99
+ from nautobot.extras.filters import ContentTypeMultipleChoiceFilter, CustomFieldFilterMixin
100
+ from nautobot.extras.models import ConfigContext
101
101
  from nautobot.extras.utils import ChangeLoggedModelsQuery, RoleModelsQuery, TaggableClassesQuery
102
102
  from nautobot.virtualization.models import VirtualMachine
103
103
 
@@ -114,17 +114,18 @@ def get_filterset_parameter_form_field(model, parameter, filterset=None):
114
114
  elif isinstance(field, NumberFilter):
115
115
  form_field = forms.IntegerField()
116
116
  elif isinstance(field, ModelMultipleChoiceFilter):
117
- related_model = Status if isinstance(field, StatusFilter) else field.extra["queryset"].model
117
+ if getattr(field, "prefers_id", False):
118
+ to_field_name = "id"
119
+ else:
120
+ to_field_name = field.extra.get("to_field_name", "id")
118
121
  form_attr = {
119
- "queryset": related_model.objects.all(),
120
- "to_field_name": field.extra.get("to_field_name", "id"),
122
+ "queryset": field.extra["queryset"],
123
+ "to_field_name": to_field_name,
124
+ "query_params": field.extra.get("query_params", {}),
121
125
  }
122
126
  # ConfigContext requires content_type set to Device and VirtualMachine
123
127
  if model == ConfigContext:
124
128
  form_attr["query_params"] = {"content_types": [Device._meta.label_lower, VirtualMachine._meta.label_lower]}
125
- # Status and Tag api requires content_type, to limit result to only related content_types
126
- elif related_model in [Role, Status, Tag]:
127
- form_attr["query_params"] = {"content_types": model._meta.label_lower}
128
129
 
129
130
  form_field = DynamicModelMultipleChoiceField(**form_attr)
130
131
  elif isinstance(
@@ -136,7 +137,9 @@ def get_filterset_parameter_form_field(model, parameter, filterset=None):
136
137
  plural_name = slugify_dashes_to_underscores(model._meta.verbose_name_plural)
137
138
  # Cable-connectable models use "cable_terminations", not "cables", as the feature name
138
139
  if plural_name == "cables":
139
- plural_name == "cable_terminations"
140
+ plural_name = "cable_terminations"
141
+ elif plural_name == "metadata_types":
142
+ plural_name = "metadata"
140
143
  try:
141
144
  form_field = MultipleContentTypeField(choices_as_strings=True, feature=plural_name)
142
145
  except KeyError:
@@ -208,6 +208,40 @@ def get_view_for_model(model, view_type=""):
208
208
  return result
209
209
 
210
210
 
211
+ def get_model_for_view_name(view_name):
212
+ """
213
+ Return the model class associated with the given view_name e.g. "circuits:circuit_detail", "dcim:device_list" and etc.
214
+ If the app_label or model_name contained by the given view_name is invalid, this will return `None`.
215
+ """
216
+ app_label, model_name = view_name.split(":") # dcim, device_list
217
+ model_name = model_name.split("_")[0] # device
218
+
219
+ try:
220
+ model = apps.get_model(app_label=app_label, model_name=model_name)
221
+ return model
222
+ except LookupError:
223
+ return None
224
+
225
+
226
+ def get_table_class_string_from_view_name(view_name):
227
+ """Return the name of the TableClass name associated with the view_name
228
+
229
+ e.g. returns `LocationTable` for view_name `dcim:location_list`
230
+
231
+ Args:
232
+ view_name (String): The name of the view e.g. dcim:location_list, circuits:circuit_list
233
+
234
+ Returns:
235
+ table_class_name (String): The name of the model table class or None e.g. LocationTable, CircuitTable
236
+ """
237
+ model = get_model_for_view_name(view_name)
238
+ if model:
239
+ table_class = get_table_for_model(model)
240
+ if table_class:
241
+ return table_class.__name__
242
+ return None
243
+
244
+
211
245
  def get_created_and_last_updated_usernames_for_model(instance):
212
246
  """
213
247
  Args:
@@ -224,8 +258,9 @@ def get_created_and_last_updated_usernames_for_model(instance):
224
258
  created_by = None
225
259
  last_updated_by = None
226
260
  try:
227
- created_by_record = object_change_records.get(action=ObjectChangeActionChoices.ACTION_CREATE)
228
- created_by = created_by_record.user_name
261
+ created_by_record = object_change_records.filter(action=ObjectChangeActionChoices.ACTION_CREATE).first()
262
+ if created_by_record is not None:
263
+ created_by = created_by_record.user_name
229
264
  except ObjectChange.DoesNotExist:
230
265
  pass
231
266
 
@@ -65,7 +65,10 @@ def convert_querydict_to_factory_formset_acceptable_querydict(request_querydict,
65
65
  # the filterset can handle the QueryDict conversion and we can just pass the QueryDict to the filterset
66
66
  # then use the FilterSet to de-dupe the field names
67
67
  lookup_field = re.sub(r"__\w+", "", filter_field_name)
68
- lookup_value = request_querydict.getlist(filter_field_name)
68
+ if isinstance(request_querydict, QueryDict):
69
+ lookup_value = request_querydict.getlist(filter_field_name)
70
+ else:
71
+ lookup_value = request_querydict.get(filter_field_name)
69
72
 
70
73
  query_dict.setlistdefault(lookup_field_placeholder % num, [lookup_field])
71
74
  query_dict.setlistdefault(lookup_type_placeholder % num, [filter_field_name])
@@ -7,14 +7,15 @@ import time
7
7
  from db_file_storage.views import get_file
8
8
  from django.apps import apps
9
9
  from django.conf import settings
10
+ from django.contrib import messages
10
11
  from django.contrib.auth.decorators import permission_required
11
- from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
12
+ from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin, UserPassesTestMixin
12
13
  from django.contrib.contenttypes.models import ContentType
13
14
  from django.http import HttpResponseForbidden, HttpResponseServerError, JsonResponse
14
15
  from django.shortcuts import get_object_or_404, render
15
16
  from django.template import loader, RequestContext, Template
16
17
  from django.template.exceptions import TemplateDoesNotExist
17
- from django.urls import resolve, reverse
18
+ from django.urls import NoReverseMatch, resolve, reverse
18
19
  from django.utils.encoding import smart_str
19
20
  from django.views.csrf import csrf_failure as _csrf_failure
20
21
  from django.views.decorators.csrf import requires_csrf_token
@@ -37,6 +38,7 @@ from rest_framework.response import Response
37
38
  from rest_framework.versioning import AcceptHeaderVersioning
38
39
  from rest_framework.views import APIView
39
40
 
41
+ from nautobot.core.celery import app
40
42
  from nautobot.core.constants import SEARCH_MAX_RESULTS
41
43
  from nautobot.core.forms import SearchForm
42
44
  from nautobot.core.releases import get_latest_release
@@ -125,6 +127,112 @@ class HomeView(AccessMixin, TemplateView):
125
127
  return self.render_to_response(context)
126
128
 
127
129
 
130
+ class WorkerStatusView(UserPassesTestMixin, TemplateView):
131
+ template_name = "utilities/worker_status.html"
132
+
133
+ def test_func(self):
134
+ return self.request.user.is_staff
135
+
136
+ def get(self, request, *args, **kwargs):
137
+ from nautobot.extras.models import JobResult
138
+ from nautobot.extras.tables import JobResultTable
139
+
140
+ # Sort queues but move the default queue to the front so that it appears first
141
+ def sort_queues(queue):
142
+ return (queue != settings.CELERY_TASK_DEFAULT_QUEUE, queue) # False sorts before true in Python sorted
143
+
144
+ # Sort workers on hostname first, then worker name -- celery worker names are in the form name@hostname (celery@worker.example.com)
145
+ def sort_workers(worker):
146
+ return list(reversed(worker.split("@")))
147
+
148
+ # Use a long timeout to retrieve the initial list of workers
149
+ max_timeout = 60
150
+ timeout = request.GET.get("timeout", "5")
151
+ if not timeout.isdigit():
152
+ timeout = 5
153
+ elif int(timeout) > max_timeout:
154
+ timeout = max_timeout
155
+ else:
156
+ timeout = int(timeout)
157
+ celery_inspect = app.control.inspect(timeout=timeout)
158
+
159
+ # stats() returns a dict of {worker_name: stats_dict}
160
+ worker_stats = celery_inspect.stats()
161
+
162
+ if worker_stats:
163
+ # Set explicit list of workers to speed up subsequent queries
164
+ celery_inspect = app.control.inspect(list(worker_stats.keys()), timeout=5.0)
165
+
166
+ # active() returns a dict of {worker_name: [task_dict, task_dict, ...]}
167
+ active_tasks = celery_inspect.active() or {}
168
+
169
+ # reserved() returns a dict of {worker_name: [task_dict, task_dict, ...]}
170
+ reserved_tasks = celery_inspect.reserved() or {}
171
+
172
+ # active_queues() returns a dict of {worker_name: [queue_dict, queue_dict, ...]}
173
+ active_queues = celery_inspect.active_queues() or {}
174
+ else:
175
+ # No workers were found, default to empty dicts for all commands
176
+ worker_stats = active_tasks = reserved_tasks = active_queues = {}
177
+
178
+ workers = []
179
+ for worker_name in sorted(worker_stats, key=sort_workers):
180
+ worker_details = worker_stats[worker_name]
181
+ active_task_job_results = JobResult.objects.filter(
182
+ id__in=[task["id"] for task in active_tasks.get(worker_name, [])]
183
+ ).only("name", "date_created")
184
+ reserved_task_job_results = JobResult.objects.filter(
185
+ id__in=[task["id"] for task in reserved_tasks.get(worker_name, [])]
186
+ ).only("name", "date_created")
187
+ running_tasks_table = JobResultTable(
188
+ active_task_job_results, exclude=["actions", "job_model", "summary", "user", "status"]
189
+ )
190
+ pending_tasks_table = JobResultTable(
191
+ reserved_task_job_results, exclude=["actions", "job_model", "summary", "user", "status"]
192
+ )
193
+
194
+ # Sort the worker's queue list
195
+ queues = sorted([queue["name"] for queue in active_queues.get(worker_name, [])], key=sort_queues)
196
+
197
+ workers.append(
198
+ {
199
+ "hostname": worker_name,
200
+ "running_tasks_table": running_tasks_table,
201
+ "pending_tasks_table": pending_tasks_table,
202
+ "queues": queues,
203
+ "uptime": worker_details["uptime"],
204
+ }
205
+ )
206
+
207
+ # Count the number of workers per queue
208
+ queue_worker_count = {
209
+ settings.CELERY_TASK_DEFAULT_QUEUE: set(),
210
+ }
211
+ for worker_name, task_queue_list in active_queues.items():
212
+ distinct_queues = {q["name"] for q in task_queue_list}
213
+ for queue in distinct_queues:
214
+ queue_worker_count.setdefault(queue, set())
215
+ queue_worker_count[queue].add(worker_name)
216
+
217
+ # Sort the queue_worker_count dictionary by queue name, then by worker hostname/name
218
+ # Then make the default queue the first entry
219
+ queue_worker_count = {
220
+ queue_name: sorted(queue_worker_count[queue_name], key=sort_workers)
221
+ for queue_name in sorted(queue_worker_count, key=sort_queues)
222
+ }
223
+
224
+ context = {
225
+ "worker_status": {
226
+ "max_timeout": max_timeout,
227
+ "queue_worker_count": queue_worker_count,
228
+ "timeout": timeout,
229
+ "workers": workers,
230
+ },
231
+ }
232
+
233
+ return self.render_to_response(context)
234
+
235
+
128
236
  class ThemePreviewView(LoginRequiredMixin, TemplateView):
129
237
  template_name = "utilities/theme_preview.html"
130
238
 
@@ -132,6 +240,8 @@ class ThemePreviewView(LoginRequiredMixin, TemplateView):
132
240
  return {
133
241
  "content_type": ContentType.objects.get_for_model(Status),
134
242
  "object": Status.objects.first(),
243
+ "verbose_name": Status.objects.all().model._meta.verbose_name,
244
+ "verbose_name_plural": Status.objects.all().model._meta.verbose_name_plural,
135
245
  }
136
246
 
137
247
 
@@ -177,28 +287,31 @@ class SearchView(AccessMixin, View):
177
287
  # corresponding to that URL, and finally the queryset, filterset, and table classes needed
178
288
  # to find and display the model search results.
179
289
  url = get_route_for_model(f"{label}.{modelname}", "list")
180
- view_func = resolve(reverse(url)).func
181
- # For a UIViewSet, view_func.cls gets what we need; for an ObjectListView, view_func.view_class is it.
182
- view_or_viewset = getattr(view_func, "cls", getattr(view_func, "view_class", None))
183
- queryset = view_or_viewset.queryset.restrict(request.user, "view")
184
- # For a UIViewSet, .filterset_class, for an ObjectListView, .filterset.
185
- filterset = getattr(view_or_viewset, "filterset_class", getattr(view_or_viewset, "filterset", None))
186
- # For a UIViewSet, .table_class, for an ObjectListView, .table.
187
- table = getattr(view_or_viewset, "table_class", getattr(view_or_viewset, "table", None))
188
-
189
- # Construct the results table for this object type
190
- filtered_queryset = filterset({"q": form.cleaned_data["q"]}, queryset=queryset).qs
191
- table = table(filtered_queryset, orderable=False)
192
- table.paginate(per_page=SEARCH_MAX_RESULTS)
193
-
194
- if table.page:
195
- results.append(
196
- {
197
- "name": queryset.model._meta.verbose_name_plural,
198
- "table": table,
199
- "url": f"{reverse(url)}?q={form.cleaned_data.get('q')}",
200
- }
201
- )
290
+ try:
291
+ view_func = resolve(reverse(url)).func
292
+ # For UIViewSet, view_func.cls gets what we need; for an ObjectListView, view_func.view_class is it.
293
+ view_or_viewset = getattr(view_func, "cls", getattr(view_func, "view_class", None))
294
+ queryset = view_or_viewset.queryset.restrict(request.user, "view")
295
+ # For a UIViewSet, .filterset_class, for an ObjectListView, .filterset.
296
+ filterset = getattr(view_or_viewset, "filterset_class", getattr(view_or_viewset, "filterset", None))
297
+ # For a UIViewSet, .table_class, for an ObjectListView, .table.
298
+ table = getattr(view_or_viewset, "table_class", getattr(view_or_viewset, "table", None))
299
+
300
+ # Construct the results table for this object type
301
+ filtered_queryset = filterset({"q": form.cleaned_data["q"]}, queryset=queryset).qs
302
+ table = table(filtered_queryset, orderable=False)
303
+ table.paginate(per_page=SEARCH_MAX_RESULTS)
304
+
305
+ if table.page:
306
+ results.append(
307
+ {
308
+ "name": queryset.model._meta.verbose_name_plural,
309
+ "table": table,
310
+ "url": f"{reverse(url)}?q={form.cleaned_data.get('q')}",
311
+ }
312
+ )
313
+ except NoReverseMatch:
314
+ messages.error(request, f'Missing URL "{url}" - unable to show search results for {modelname}.')
202
315
 
203
316
  return render(
204
317
  request,