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

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 +111 -0
  26. nautobot/cloud/filters.py +184 -0
  27. nautobot/cloud/forms.py +333 -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 +247 -0
  32. nautobot/cloud/navigation.py +85 -0
  33. nautobot/cloud/tables.py +173 -0
  34. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
  35. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +128 -0
  36. nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
  37. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +100 -0
  38. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +65 -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 +113 -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 +96 -28
  57. nautobot/core/forms/fields.py +10 -4
  58. nautobot/core/forms/forms.py +1 -1
  59. nautobot/core/forms/widgets.py +18 -1
  60. nautobot/core/graphql/generators.py +2 -2
  61. nautobot/core/graphql/schema.py +34 -4
  62. nautobot/core/jobs/__init__.py +17 -6
  63. nautobot/core/jobs/cleanup.py +100 -0
  64. nautobot/core/jobs/groups.py +38 -0
  65. nautobot/core/management/commands/generate_test_data.py +116 -3
  66. nautobot/core/models/__init__.py +34 -9
  67. nautobot/core/models/generics.py +19 -3
  68. nautobot/core/models/name_color_content_types.py +7 -28
  69. nautobot/core/models/querysets.py +4 -3
  70. nautobot/core/models/tree_queries.py +1 -1
  71. nautobot/core/models/utils.py +21 -5
  72. nautobot/core/settings.py +4 -30
  73. nautobot/core/settings.yaml +34 -27
  74. nautobot/core/settings_funcs.py +103 -0
  75. nautobot/core/tables.py +127 -56
  76. nautobot/core/templates/admin/search_form.html +1 -1
  77. nautobot/core/templates/buttons/add.html +11 -3
  78. nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
  79. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
  80. nautobot/core/templates/buttons/export.html +101 -53
  81. nautobot/core/templates/buttons/job_import.html +11 -3
  82. nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
  83. nautobot/core/templates/generic/object_bulk_update.html +3 -1
  84. nautobot/core/templates/generic/object_changelog.html +0 -9
  85. nautobot/core/templates/generic/object_list.html +156 -17
  86. nautobot/core/templates/generic/object_retrieve.html +80 -16
  87. nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
  88. nautobot/core/templates/inc/javascript.html +2 -0
  89. nautobot/core/templates/inc/media.html +2 -2
  90. nautobot/core/templates/inc/nav_menu.html +1 -0
  91. nautobot/core/templates/inc/paginator.html +7 -7
  92. nautobot/core/templates/inc/search_panel.html +2 -2
  93. nautobot/core/templates/inc/table.html +2 -2
  94. nautobot/core/templates/nautobot_config.py.j2 +13 -23
  95. nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
  96. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
  97. nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
  98. nautobot/core/templates/utilities/theme_preview.html +25 -8
  99. nautobot/core/templates/utilities/worker_status.html +152 -0
  100. nautobot/core/templatetags/buttons.py +335 -38
  101. nautobot/core/templatetags/form_helpers.py +1 -1
  102. nautobot/core/templatetags/helpers.py +181 -11
  103. nautobot/core/testing/api.py +5 -4
  104. nautobot/core/testing/filters.py +51 -13
  105. nautobot/core/testing/mixins.py +46 -0
  106. nautobot/core/testing/models.py +22 -0
  107. nautobot/core/testing/schema.py +4 -8
  108. nautobot/core/testing/views.py +31 -14
  109. nautobot/core/tests/integration/test_general_functionality.py +1 -1
  110. nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
  111. nautobot/core/tests/integration/test_swagger.py +1 -1
  112. nautobot/core/tests/nautobot_config.py +0 -1
  113. nautobot/core/tests/runner.py +2 -2
  114. nautobot/core/tests/test_api.py +1 -0
  115. nautobot/core/tests/test_authentication.py +7 -2
  116. nautobot/core/tests/test_filters.py +11 -9
  117. nautobot/core/tests/test_forms.py +9 -0
  118. nautobot/core/tests/test_graphql.py +27 -16
  119. nautobot/core/tests/test_jobs.py +123 -74
  120. nautobot/core/tests/test_tables.py +3 -1
  121. nautobot/core/tests/test_templatetags_helpers.py +12 -5
  122. nautobot/core/tests/test_utils.py +31 -20
  123. nautobot/core/tests/test_views.py +6 -6
  124. nautobot/core/urls.py +8 -3
  125. nautobot/core/utils/deprecation.py +29 -0
  126. nautobot/core/utils/filtering.py +12 -9
  127. nautobot/core/utils/lookup.py +37 -2
  128. nautobot/core/utils/requests.py +4 -1
  129. nautobot/core/views/__init__.py +137 -24
  130. nautobot/core/views/generic.py +118 -66
  131. nautobot/core/views/mixins.py +104 -35
  132. nautobot/core/views/paginator.py +9 -3
  133. nautobot/core/views/renderers.py +121 -56
  134. nautobot/core/views/utils.py +79 -1
  135. nautobot/dcim/__init__.py +0 -1
  136. nautobot/dcim/api/serializers.py +180 -44
  137. nautobot/dcim/api/urls.py +7 -3
  138. nautobot/dcim/api/views.py +53 -7
  139. nautobot/dcim/apps.py +3 -0
  140. nautobot/dcim/choices.py +25 -0
  141. nautobot/dcim/constants.py +7 -0
  142. nautobot/dcim/factory.py +249 -18
  143. nautobot/dcim/filters/__init__.py +369 -193
  144. nautobot/dcim/filters/mixins.py +274 -1
  145. nautobot/dcim/forms.py +817 -109
  146. nautobot/dcim/graphql/types.py +2 -2
  147. nautobot/dcim/homepage.py +1 -1
  148. nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
  149. nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
  150. nautobot/dcim/migrations/0061_module_models.py +861 -0
  151. nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
  152. nautobot/dcim/models/__init__.py +8 -0
  153. nautobot/dcim/models/cables.py +15 -0
  154. nautobot/dcim/models/device_component_templates.py +207 -53
  155. nautobot/dcim/models/device_components.py +275 -106
  156. nautobot/dcim/models/devices.py +466 -13
  157. nautobot/dcim/navigation.py +47 -0
  158. nautobot/dcim/signals.py +3 -3
  159. nautobot/dcim/tables/__init__.py +35 -23
  160. nautobot/dcim/tables/devices.py +231 -59
  161. nautobot/dcim/tables/devicetypes.py +65 -9
  162. nautobot/dcim/tables/racks.py +5 -1
  163. nautobot/dcim/tables/template_code.py +46 -26
  164. nautobot/dcim/templates/dcim/cable_connect.html +76 -3
  165. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
  166. nautobot/dcim/templates/dcim/device/base.html +15 -7
  167. nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
  168. nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
  169. nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
  170. nautobot/dcim/templates/dcim/device/frontports.html +2 -3
  171. nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
  172. nautobot/dcim/templates/dcim/device/inventory.html +2 -3
  173. nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
  174. nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
  175. nautobot/dcim/templates/dcim/device/powerports.html +2 -3
  176. nautobot/dcim/templates/dcim/device/rearports.html +2 -3
  177. nautobot/dcim/templates/dcim/device.html +45 -1
  178. nautobot/dcim/templates/dcim/device_component.html +13 -5
  179. nautobot/dcim/templates/dcim/device_list.html +2 -1
  180. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -6
  181. nautobot/dcim/templates/dcim/devicetype.html +99 -98
  182. nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
  183. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  184. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
  185. nautobot/dcim/templates/dcim/interface.html +17 -2
  186. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
  187. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  188. nautobot/dcim/templates/dcim/manufacturer.html +24 -0
  189. nautobot/dcim/templates/dcim/module/base.html +97 -0
  190. nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
  191. nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
  192. nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
  193. nautobot/dcim/templates/dcim/module_destroy.html +5 -0
  194. nautobot/dcim/templates/dcim/module_frontports.html +53 -0
  195. nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
  196. nautobot/dcim/templates/dcim/module_list.html +20 -0
  197. nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
  198. nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
  199. nautobot/dcim/templates/dcim/module_powerports.html +53 -0
  200. nautobot/dcim/templates/dcim/module_rearports.html +53 -0
  201. nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
  202. nautobot/dcim/templates/dcim/module_update.html +71 -0
  203. nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
  204. nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
  205. nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
  206. nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
  207. nautobot/dcim/templates/dcim/moduletype_retrieve.html +142 -0
  208. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
  209. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
  210. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  211. nautobot/dcim/tests/test_api.py +691 -208
  212. nautobot/dcim/tests/test_filters.py +836 -217
  213. nautobot/dcim/tests/test_models.py +1072 -39
  214. nautobot/dcim/tests/test_views.py +1488 -358
  215. nautobot/dcim/urls.py +17 -2
  216. nautobot/dcim/utils.py +2 -3
  217. nautobot/dcim/views.py +1107 -120
  218. nautobot/extras/__init__.py +0 -1
  219. nautobot/extras/api/serializers.py +115 -3
  220. nautobot/extras/api/urls.py +12 -0
  221. nautobot/extras/api/views.py +125 -7
  222. nautobot/extras/apps.py +2 -2
  223. nautobot/extras/choices.py +43 -0
  224. nautobot/extras/context_managers.py +13 -8
  225. nautobot/extras/datasources/git.py +2 -0
  226. nautobot/extras/factory.py +422 -9
  227. nautobot/extras/filters/__init__.py +174 -3
  228. nautobot/extras/filters/mixins.py +46 -43
  229. nautobot/extras/forms/base.py +17 -4
  230. nautobot/extras/forms/forms.py +227 -8
  231. nautobot/extras/forms/mixins.py +93 -0
  232. nautobot/extras/graphql/types.py +23 -10
  233. nautobot/extras/homepage.py +16 -13
  234. nautobot/extras/jobs.py +2 -2
  235. nautobot/extras/management/__init__.py +1 -0
  236. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
  237. nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
  238. nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
  239. nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
  240. nautobot/extras/migrations/0111_metadata.py +162 -0
  241. nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
  242. nautobot/extras/migrations/0113_saved_views.py +77 -0
  243. nautobot/extras/models/__init__.py +15 -1
  244. nautobot/extras/models/change_logging.py +3 -3
  245. nautobot/extras/models/contacts.py +4 -0
  246. nautobot/extras/models/customfields.py +18 -3
  247. nautobot/extras/models/groups.py +389 -225
  248. nautobot/extras/models/jobs.py +4 -84
  249. nautobot/extras/models/metadata.py +441 -0
  250. nautobot/extras/models/mixins.py +72 -62
  251. nautobot/extras/models/models.py +116 -9
  252. nautobot/extras/models/relationships.py +9 -2
  253. nautobot/extras/models/tags.py +13 -2
  254. nautobot/extras/navigation.py +57 -0
  255. nautobot/extras/plugins/__init__.py +3 -1
  256. nautobot/extras/querysets.py +30 -66
  257. nautobot/extras/signals.py +96 -114
  258. nautobot/extras/tables.py +171 -47
  259. nautobot/extras/templates/extras/dynamicgroup.html +44 -15
  260. nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
  261. nautobot/extras/templates/extras/job.html +1 -1
  262. nautobot/extras/templates/extras/job_detail.html +0 -11
  263. nautobot/extras/templates/extras/jobresult.html +61 -74
  264. nautobot/extras/templates/extras/metadatatype_create.html +89 -0
  265. nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
  266. nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
  267. nautobot/extras/templates/extras/objectchange_list.html +0 -12
  268. nautobot/extras/templates/extras/plugins_list.html +1 -3
  269. nautobot/extras/templates/extras/role_retrieve.html +48 -0
  270. nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
  271. nautobot/extras/tests/integration/test_customfields.py +1 -0
  272. nautobot/extras/tests/test_api.py +501 -22
  273. nautobot/extras/tests/test_changelog.py +20 -9
  274. nautobot/extras/tests/test_context_managers.py +22 -15
  275. nautobot/extras/tests/test_datasources.py +13 -1
  276. nautobot/extras/tests/test_dynamicgroups.py +201 -171
  277. nautobot/extras/tests/test_filters.py +211 -12
  278. nautobot/extras/tests/test_jobs.py +4 -4
  279. nautobot/extras/tests/test_models.py +499 -4
  280. nautobot/extras/tests/test_relationships.py +1 -0
  281. nautobot/extras/tests/test_views.py +565 -28
  282. nautobot/extras/tests/test_webhooks.py +1 -1
  283. nautobot/extras/urls.py +5 -0
  284. nautobot/extras/utils.py +56 -45
  285. nautobot/extras/views.py +585 -96
  286. nautobot/ipam/__init__.py +0 -1
  287. nautobot/ipam/apps.py +1 -0
  288. nautobot/ipam/factory.py +17 -19
  289. nautobot/ipam/filters.py +14 -1
  290. nautobot/ipam/forms.py +9 -5
  291. nautobot/ipam/graphql/types.py +2 -2
  292. nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
  293. nautobot/ipam/models.py +23 -9
  294. nautobot/ipam/querysets.py +1 -1
  295. nautobot/ipam/signals.py +4 -2
  296. nautobot/ipam/tables.py +1 -0
  297. nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
  298. nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
  299. nautobot/ipam/templates/ipam/prefix.html +1 -0
  300. nautobot/ipam/tests/test_api.py +37 -18
  301. nautobot/ipam/tests/test_filters.py +26 -2
  302. nautobot/ipam/tests/test_models.py +8 -3
  303. nautobot/ipam/tests/test_querysets.py +1 -1
  304. nautobot/ipam/tests/test_views.py +3 -2
  305. nautobot/ipam/urls.py +2 -2
  306. nautobot/ipam/views.py +25 -28
  307. nautobot/project-static/css/base.css +20 -1
  308. nautobot/project-static/css/dark.css +11 -0
  309. nautobot/project-static/docs/404.html +884 -80
  310. nautobot/project-static/docs/apps/index.html +884 -80
  311. nautobot/project-static/docs/apps/nautobot-apps.html +884 -80
  312. nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
  313. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +911 -112
  314. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +896 -93
  315. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1457 -790
  316. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +927 -136
  317. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +969 -180
  318. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +893 -91
  319. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +889 -85
  320. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +983 -185
  321. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +938 -143
  322. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1064 -274
  323. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1190 -346
  324. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1663 -865
  325. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1156 -373
  326. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2200 -1502
  327. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2229 -1421
  328. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +904 -103
  329. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +955 -155
  330. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1002 -215
  331. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1911 -1275
  332. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1835 -1091
  333. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +896 -93
  334. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2323 -1693
  335. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1785 -1023
  336. nautobot/project-static/docs/development/apps/api/configuration-view.html +884 -80
  337. nautobot/project-static/docs/development/apps/api/database-backend-config.html +884 -80
  338. nautobot/project-static/docs/development/apps/api/models/django-admin.html +884 -80
  339. nautobot/project-static/docs/development/apps/api/models/global-search.html +884 -80
  340. nautobot/project-static/docs/development/apps/api/models/graphql.html +884 -80
  341. nautobot/project-static/docs/development/apps/api/models/index.html +922 -81
  342. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +884 -80
  343. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +884 -80
  344. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +884 -80
  345. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +884 -80
  346. nautobot/project-static/docs/development/apps/api/platform-features/index.html +884 -80
  347. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +884 -80
  348. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +884 -80
  349. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +884 -80
  350. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +884 -80
  351. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +884 -80
  352. nautobot/project-static/docs/development/apps/api/prometheus.html +884 -80
  353. nautobot/project-static/docs/development/apps/api/setup.html +884 -80
  354. nautobot/project-static/docs/development/apps/api/testing.html +884 -80
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +884 -80
  356. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +884 -80
  357. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +884 -80
  358. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +884 -80
  359. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +884 -80
  360. nautobot/project-static/docs/development/apps/api/views/base-template.html +884 -80
  361. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +884 -80
  362. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +884 -80
  363. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +884 -80
  364. nautobot/project-static/docs/development/apps/api/views/index.html +884 -80
  365. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +884 -80
  366. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +884 -80
  367. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +884 -80
  368. nautobot/project-static/docs/development/apps/api/views/notes.html +884 -80
  369. nautobot/project-static/docs/development/apps/api/views/rest-api.html +884 -80
  370. nautobot/project-static/docs/development/apps/api/views/urls.html +884 -80
  371. nautobot/project-static/docs/development/apps/index.html +884 -80
  372. nautobot/project-static/docs/development/apps/migration/code-updates.html +884 -80
  373. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +884 -80
  374. nautobot/project-static/docs/development/apps/migration/from-v1.html +884 -80
  375. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +884 -80
  376. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +884 -80
  377. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +884 -80
  378. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +884 -80
  379. nautobot/project-static/docs/development/apps/porting-from-netbox.html +884 -80
  380. nautobot/project-static/docs/development/core/application-registry.html +884 -80
  381. nautobot/project-static/docs/development/core/best-practices.html +885 -80
  382. nautobot/project-static/docs/development/core/bootstrap-ui.html +884 -80
  383. nautobot/project-static/docs/development/core/caching.html +884 -80
  384. nautobot/project-static/docs/development/core/controllers.html +884 -80
  385. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +884 -80
  386. nautobot/project-static/docs/development/core/generic-views.html +884 -80
  387. nautobot/project-static/docs/development/core/getting-started.html +884 -80
  388. nautobot/project-static/docs/development/core/homepage.html +884 -80
  389. nautobot/project-static/docs/development/core/index.html +884 -91
  390. nautobot/project-static/docs/development/core/model-checklist.html +887 -81
  391. nautobot/project-static/docs/development/core/model-features.html +884 -80
  392. nautobot/project-static/docs/development/core/natural-keys.html +884 -80
  393. nautobot/project-static/docs/development/core/navigation-menu.html +884 -80
  394. nautobot/project-static/docs/development/core/release-checklist.html +887 -83
  395. nautobot/project-static/docs/development/core/role-internals.html +884 -80
  396. nautobot/project-static/docs/development/core/settings.html +884 -80
  397. nautobot/project-static/docs/development/core/style-guide.html +885 -81
  398. nautobot/project-static/docs/development/core/templates.html +896 -81
  399. nautobot/project-static/docs/development/core/testing.html +884 -80
  400. nautobot/project-static/docs/development/core/user-preferences.html +884 -80
  401. nautobot/project-static/docs/development/index.html +884 -80
  402. nautobot/project-static/docs/development/jobs/index.html +1247 -457
  403. nautobot/project-static/docs/development/jobs/migration/from-v1.html +884 -80
  404. nautobot/project-static/docs/index.html +13 -8228
  405. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
  406. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
  407. nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
  408. nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
  409. nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
  410. nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
  411. nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
  412. nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
  413. nautobot/project-static/docs/models/dcim/module.html +15 -0
  414. nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
  415. nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
  416. nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
  417. nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
  418. nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
  419. nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
  420. nautobot/project-static/docs/models/extras/role.html +15 -0
  421. nautobot/project-static/docs/models/extras/savedview.html +15 -0
  422. nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
  423. nautobot/project-static/docs/models/extras/status.html +15 -0
  424. nautobot/project-static/docs/objects.inv +0 -0
  425. nautobot/project-static/docs/overview/application_stack.html +892 -81
  426. nautobot/project-static/docs/overview/design_philosophy.html +886 -82
  427. nautobot/project-static/docs/overview/index.html +9032 -13
  428. nautobot/project-static/docs/release-notes/index.html +887 -83
  429. nautobot/project-static/docs/release-notes/version-1.0.html +884 -80
  430. nautobot/project-static/docs/release-notes/version-1.1.html +884 -80
  431. nautobot/project-static/docs/release-notes/version-1.2.html +884 -80
  432. nautobot/project-static/docs/release-notes/version-1.3.html +884 -80
  433. nautobot/project-static/docs/release-notes/version-1.4.html +884 -80
  434. nautobot/project-static/docs/release-notes/version-1.5.html +885 -81
  435. nautobot/project-static/docs/release-notes/version-1.6.html +885 -81
  436. nautobot/project-static/docs/release-notes/version-2.0.html +884 -80
  437. nautobot/project-static/docs/release-notes/version-2.1.html +884 -80
  438. nautobot/project-static/docs/release-notes/version-2.2.html +990 -323
  439. nautobot/project-static/docs/release-notes/version-2.3.html +9524 -0
  440. nautobot/project-static/docs/requirements.txt +4 -4
  441. nautobot/project-static/docs/search/search_index.json +1 -1
  442. nautobot/project-static/docs/sitemap.xml +335 -260
  443. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  444. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +884 -80
  445. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +884 -80
  446. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +884 -80
  447. nautobot/project-static/docs/user-guide/administration/configuration/index.html +884 -80
  448. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +983 -197
  449. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +884 -80
  450. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +884 -80
  451. nautobot/project-static/docs/user-guide/administration/guides/caching.html +884 -80
  452. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +888 -84
  453. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +884 -80
  454. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +884 -80
  455. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +884 -80
  456. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +884 -80
  457. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +884 -80
  458. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +884 -80
  459. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +884 -80
  460. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +884 -80
  461. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +884 -80
  462. nautobot/project-static/docs/user-guide/administration/installation/index.html +888 -80
  463. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +884 -80
  464. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +884 -80
  465. nautobot/project-static/docs/user-guide/administration/installation/services.html +888 -80
  466. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +900 -91
  467. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +884 -80
  468. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +884 -80
  469. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +884 -80
  470. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +884 -80
  471. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +915 -163
  472. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +884 -80
  473. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +884 -80
  474. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +884 -80
  475. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +884 -80
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +884 -80
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +884 -80
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +884 -80
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +884 -80
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +884 -80
  481. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +884 -80
  482. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +884 -80
  483. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +885 -81
  484. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +884 -80
  485. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +888 -80
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +887 -83
  487. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
  488. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
  489. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
  494. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +898 -94
  495. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +915 -97
  496. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +915 -97
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +910 -92
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +915 -97
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +898 -94
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +898 -94
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +905 -97
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +912 -108
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +913 -109
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +910 -106
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +898 -94
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +906 -97
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +918 -100
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +928 -110
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +920 -98
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +898 -94
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +929 -111
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +920 -102
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +910 -106
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +913 -109
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +914 -106
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +908 -104
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +898 -94
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +932 -75
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +916 -98
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +898 -94
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +935 -78
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +913 -95
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +921 -117
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +910 -106
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +898 -94
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +914 -96
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +916 -98
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +898 -94
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +898 -94
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +898 -94
  535. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +884 -80
  536. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +884 -80
  537. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +889 -81
  538. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +889 -81
  539. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +884 -80
  540. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +884 -80
  541. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +884 -80
  542. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +884 -80
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +884 -80
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +884 -80
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +884 -80
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +884 -80
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +884 -80
  548. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +884 -80
  549. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +893 -88
  550. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +884 -80
  551. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +884 -80
  552. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +884 -80
  553. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +884 -80
  554. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +884 -80
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +889 -81
  556. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +884 -80
  557. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +884 -80
  558. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +884 -80
  559. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +884 -80
  560. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +884 -80
  561. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +884 -80
  562. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +884 -80
  563. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +884 -80
  564. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +884 -80
  565. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +884 -80
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +884 -80
  567. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +884 -80
  568. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +884 -80
  569. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
  570. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
  571. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
  572. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
  573. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
  574. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
  575. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
  576. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
  587. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
  589. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
  590. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
  591. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
  592. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
  593. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
  594. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +884 -80
  595. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +884 -80
  596. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +884 -80
  597. nautobot/project-static/docs/user-guide/index.html +884 -80
  598. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +884 -80
  599. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +884 -80
  600. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +884 -80
  601. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +884 -80
  602. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1250 -777
  603. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +887 -83
  604. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +884 -80
  605. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +884 -80
  606. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +884 -80
  607. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +884 -80
  608. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +884 -80
  609. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +884 -80
  610. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +887 -83
  611. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +884 -80
  612. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +884 -80
  613. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +888 -80
  614. nautobot/project-static/docs/user-guide/platform-functionality/metadata.html +8948 -0
  615. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +887 -83
  616. nautobot/project-static/docs/user-guide/platform-functionality/note.html +884 -80
  617. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +884 -80
  618. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +884 -80
  619. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +884 -80
  620. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +884 -80
  621. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +884 -80
  622. nautobot/project-static/docs/user-guide/platform-functionality/role.html +887 -83
  623. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
  624. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +887 -83
  625. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
  626. nautobot/project-static/docs/user-guide/platform-functionality/status.html +884 -80
  627. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +884 -80
  628. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +942 -113
  629. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +884 -80
  630. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +884 -80
  631. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +884 -80
  632. nautobot/project-static/js/forms.js +71 -0
  633. nautobot/project-static/js/table_sorting_indicator.js +46 -0
  634. nautobot/project-static/js/tableconfig.js +6 -1
  635. nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
  636. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
  637. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
  638. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
  639. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
  640. nautobot/tenancy/__init__.py +0 -1
  641. nautobot/tenancy/apps.py +1 -0
  642. nautobot/tenancy/factory.py +3 -2
  643. nautobot/tenancy/filters/__init__.py +1 -0
  644. nautobot/tenancy/forms.py +1 -1
  645. nautobot/tenancy/templates/tenancy/tenant.html +22 -18
  646. nautobot/tenancy/views.py +11 -10
  647. nautobot/users/__init__.py +0 -1
  648. nautobot/users/api/serializers.py +1 -1
  649. nautobot/users/api/views.py +4 -2
  650. nautobot/users/apps.py +3 -2
  651. nautobot/users/factory.py +3 -3
  652. nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
  653. nautobot/users/models.py +12 -0
  654. nautobot/users/tests/test_filters.py +6 -3
  655. nautobot/users/urls.py +8 -0
  656. nautobot/virtualization/__init__.py +0 -1
  657. nautobot/virtualization/apps.py +1 -0
  658. nautobot/virtualization/filters.py +6 -1
  659. nautobot/virtualization/forms.py +11 -3
  660. nautobot/virtualization/graphql/types.py +2 -2
  661. nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
  662. nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
  663. nautobot/virtualization/tables.py +15 -5
  664. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  665. nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
  666. nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
  667. nautobot/virtualization/tests/test_api.py +9 -4
  668. nautobot/virtualization/tests/test_filters.py +22 -0
  669. nautobot/virtualization/tests/test_models.py +7 -3
  670. nautobot/virtualization/tests/test_views.py +19 -3
  671. nautobot/virtualization/urls.py +2 -2
  672. nautobot/virtualization/views.py +10 -32
  673. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/METADATA +21 -19
  674. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/RECORD +679 -559
  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.0b1.dist-info}/LICENSE.txt +0 -0
  695. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/NOTICE +0 -0
  696. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/WHEEL +0 -0
  697. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/entry_points.txt +0 -0
nautobot/core/filters.py CHANGED
@@ -1,4 +1,3 @@
1
- from collections import OrderedDict
2
1
  from copy import deepcopy
3
2
  import logging
4
3
  import uuid
@@ -7,11 +6,9 @@ from django import forms as django_forms
7
6
  from django.conf import settings
8
7
  from django.db import models
9
8
  from django.forms.utils import ErrorDict, ErrorList
10
- from django.utils.encoding import force_str
11
- from django.utils.text import capfirst
12
9
  import django_filters
13
10
  from django_filters.constants import EMPTY_VALUES
14
- from django_filters.utils import get_model_field, label_for_filter, resolve_field, verbose_lookup_expr
11
+ from django_filters.utils import get_model_field, resolve_field
15
12
  from drf_spectacular.types import OpenApiTypes
16
13
  from drf_spectacular.utils import extend_schema_field
17
14
 
@@ -125,8 +122,8 @@ class MultiValueUUIDFilter(django_filters.UUIDFilter, django_filters.MultipleCho
125
122
 
126
123
  class RelatedMembershipBooleanFilter(django_filters.BooleanFilter):
127
124
  """
128
- BooleanFilter for related objects that will explicitly perform `exclude=True` and `isnull`
129
- lookups. The `field_name` argument is required and must be set to the related field on the
125
+ BooleanFilter for related objects that will explicitly perform `isnull` lookups.
126
+ The `field_name` argument is required and must be set to the related field on the
130
127
  model.
131
128
 
132
129
  This should be used instead of a default `BooleanFilter` paired `method=`
@@ -134,15 +131,33 @@ class RelatedMembershipBooleanFilter(django_filters.BooleanFilter):
134
131
 
135
132
  Example:
136
133
 
137
- has_interfaces = RelatedMembershipBooleanFilter(
138
- field_name="interfaces",
139
- label="Has interfaces",
134
+ has_modules = RelatedMembershipBooleanFilter(
135
+ field_name="module_bays__installed_module",
136
+ label="Has modules",
140
137
  )
138
+
139
+ This would generate a filter that returns instances that have at least one module
140
+ bay with an installed module. The `has_modules=False` filter would exclude instances
141
+ with at least one module bay with an installed module.
142
+
143
+ Set `exclude=True` to reverse the behavior of the filter. This __may__ be useful
144
+ for filtering on null directly related fields but this filter is not smart enough
145
+ to differentiate between `fieldA__fieldB__isnull` and `fieldA__isnull` so it's not
146
+ suitable for cases like the `has_empty_module_bays` filter where an instance may
147
+ not have any module bays.
148
+
149
+ See the below table for more information:
150
+
151
+ | value | exclude | Result
152
+ |-------------|------------------|-------
153
+ | True | False (default) | Return instances with at least one non-null match -- qs.filter(field_name__isnull=False)
154
+ | False | False (default) | Exclude instances with at least one non-null match -- qs.exclude(field_name__isnull=False)
155
+ | True | True | Return instances with at least one null match -- qs.filter(field_name__isnull=True)
156
+ | False | True | Exclude instances with at least one null match -- qs.exclude(field_name__isnull=True)
157
+
141
158
  """
142
159
 
143
- def __init__(
144
- self, field_name=None, lookup_expr="isnull", *, label=None, method=None, distinct=False, exclude=True, **kwargs
145
- ):
160
+ def __init__(self, field_name=None, lookup_expr="isnull", *, label=None, method=None, distinct=True, **kwargs):
146
161
  if field_name is None:
147
162
  raise ValueError(f"Field name is required for {self.__class__.__name__}")
148
163
 
@@ -152,11 +167,23 @@ class RelatedMembershipBooleanFilter(django_filters.BooleanFilter):
152
167
  label=label,
153
168
  method=method,
154
169
  distinct=distinct,
155
- exclude=exclude,
156
170
  widget=forms.StaticSelect2(choices=forms.BOOLEAN_CHOICES),
157
171
  **kwargs,
158
172
  )
159
173
 
174
+ def filter(self, qs, value):
175
+ if value in EMPTY_VALUES:
176
+ return qs
177
+ if self.distinct:
178
+ qs = qs.distinct()
179
+ lookup = f"{self.field_name}__{self.lookup_expr}"
180
+ if bool(value):
181
+ # if self.exclude=False, return instances with field populated
182
+ return qs.filter(**{lookup: self.exclude})
183
+ else:
184
+ # if self.exclude=False, exclude instances with field populated
185
+ return qs.exclude(**{lookup: self.exclude})
186
+
160
187
 
161
188
  class NumericArrayFilter(django_filters.NumberFilter):
162
189
  """
@@ -403,12 +430,22 @@ class NaturalKeyOrPKMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilte
403
430
  Filter that supports filtering on values matching the `pk` field and another
404
431
  field of a foreign-key related object. The desired field is set using the `to_field_name`
405
432
  keyword argument on filter initialization (defaults to `name`).
433
+
434
+ NOTE that the `to_field_name` field does not have to be a "true" natural key (ie. unique), it
435
+ was just the best name we could come up with for this filter
436
+
406
437
  """
407
438
 
408
439
  field_class = forms.MultiMatchModelMultipleChoiceField
409
440
 
410
- def __init__(self, *args, **kwargs):
441
+ def __init__(self, *args, prefers_id=False, **kwargs):
442
+ """Initialize the NaturalKeyOrPKMultipleChoiceFilter.
443
+
444
+ Args:
445
+ prefers_id (bool, optional): Prefer PK (ID) over the 'to_field_name'. Defaults to False.
446
+ """
411
447
  self.natural_key = kwargs.setdefault("to_field_name", "name")
448
+ self.prefers_id = prefers_id
412
449
  super().__init__(*args, **kwargs)
413
450
 
414
451
  def get_filter_predicate(self, v):
@@ -683,18 +720,6 @@ class BaseFilterSet(django_filters.FilterSet):
683
720
  # Of course setting the negation of the existing filter's exclude attribute handles both cases
684
721
  new_filter.exclude = not filter_field.exclude
685
722
 
686
- # If the base filter_field has a custom label, django_filters won't adjust it for the new_filter lookup,
687
- # so we have to do it.
688
- if filter_field.label and filter_field.label != label_for_filter(
689
- cls.Meta.model, filter_field.field_name, filter_field.lookup_expr, filter_field.exclude
690
- ):
691
- # Lightly adjusted from label_for_filter() implementation:
692
- verbose_expression = ["exclude", filter_field.label] if new_filter.exclude else [filter_field.label]
693
- if isinstance(lookup_expr, str):
694
- verbose_expression.append(verbose_lookup_expr(lookup_expr))
695
- verbose_expression = [force_str(part) for part in verbose_expression if part]
696
- new_filter.label = capfirst(" ".join(verbose_expression))
697
-
698
723
  magic_filters[new_filter_name] = new_filter
699
724
 
700
725
  return magic_filters
@@ -725,8 +750,8 @@ class BaseFilterSet(django_filters.FilterSet):
725
750
  def get_fields(cls):
726
751
  fields = super().get_fields()
727
752
  if "id" not in fields and (cls._meta.exclude is None or "id" not in cls._meta.exclude):
728
- # Add "id" as the first key in the `fields` OrderedDict
729
- fields = OrderedDict(id=[django_filters.conf.settings.DEFAULT_LOOKUP_EXPR], **fields)
753
+ # Add "id" as the first key in the `fields` dict
754
+ fields = {"id": [django_filters.conf.settings.DEFAULT_LOOKUP_EXPR], **fields}
730
755
  return fields
731
756
 
732
757
  @classmethod
@@ -741,8 +766,51 @@ class BaseFilterSet(django_filters.FilterSet):
741
766
  if filter_name.startswith("_"):
742
767
  del filters[filter_name]
743
768
 
769
+ if getattr(cls._meta.model, "is_contact_associable_model", False):
770
+ # Add "contacts" and "teams" filters
771
+ from nautobot.extras.models import Contact, Team
772
+
773
+ if "contacts" not in filters:
774
+ filters["contacts"] = NaturalKeyOrPKMultipleChoiceFilter(
775
+ queryset=Contact.objects.all(),
776
+ field_name="associated_contacts__contact",
777
+ to_field_name="name",
778
+ label="Contacts (name or ID)",
779
+ )
780
+
781
+ if "teams" not in filters:
782
+ filters["teams"] = NaturalKeyOrPKMultipleChoiceFilter(
783
+ queryset=Team.objects.all(),
784
+ field_name="associated_contacts__team",
785
+ to_field_name="name",
786
+ label="Teams (name or ID)",
787
+ )
788
+
789
+ if "dynamic_groups" not in filters and getattr(cls._meta.model, "is_dynamic_group_associable_model", False):
790
+ if not hasattr(cls._meta.model, "static_group_association_set"):
791
+ logger.warning(
792
+ "Model %s has 'is_dynamic_group_associable_model = True' but lacks "
793
+ "a 'static_group_association_set' attribute. Perhaps this is due to it inheriting from "
794
+ "the deprecated DynamicGroupMixin class instead of the preferred DynamicGroupsModelMixin?",
795
+ cls._meta.model,
796
+ )
797
+ else:
798
+ # Add "dynamic_groups" field as the last key
799
+ from nautobot.extras.models import DynamicGroup
800
+
801
+ filters["dynamic_groups"] = NaturalKeyOrPKMultipleChoiceFilter(
802
+ queryset=DynamicGroup.objects.all(),
803
+ field_name="static_group_association_set__dynamic_group",
804
+ to_field_name="name",
805
+ query_params={"content_type": cls._meta.model._meta.label_lower},
806
+ label="Dynamic groups (name or ID)",
807
+ )
808
+
744
809
  # django-filters has no concept of "abstract" filtersets, so we have to fake it
745
810
  if cls._meta.model is not None:
811
+ if "tags" in filters and isinstance(filters["tags"], TagFilter):
812
+ filters["tags"].extra["query_params"] = {"content_types": [cls._meta.model._meta.label_lower]}
813
+
746
814
  new_filters = {}
747
815
  for existing_filter_name, existing_filter in filters.items():
748
816
  new_filters.update(
@@ -228,6 +228,10 @@ class MultipleContentTypeField(django_forms.ModelMultipleChoiceField):
228
228
 
229
229
  if "queryset" not in kwargs:
230
230
  if feature is not None:
231
+ from nautobot.extras.registry import registry
232
+
233
+ if feature not in registry["model_features"]:
234
+ raise KeyError
231
235
  kwargs["queryset"] = ContentType.objects.filter(
232
236
  extras_utils.FeatureQuery(feature).get_query()
233
237
  ).order_by("app_label", "model")
@@ -453,6 +457,7 @@ class DynamicModelChoiceMixin:
453
457
 
454
458
  filter = django_filters.ModelChoiceFilter # 2.0 TODO(Glenn): can we rename this? pylint: disable=redefined-builtin
455
459
  widget = widgets.APISelect
460
+ iterator = widgets.MinimalModelChoiceIterator
456
461
 
457
462
  def __init__(
458
463
  self,
@@ -475,6 +480,7 @@ class DynamicModelChoiceMixin:
475
480
  # to_field_name is set by ModelChoiceField.__init__(), but we need to set it early for reference
476
481
  # by widget_attrs()
477
482
  self.to_field_name = kwargs.get("to_field_name")
483
+ self.data_queryset = kwargs.get("queryset") # may be updated in get_bound_field()
478
484
 
479
485
  super().__init__(*args, **kwargs)
480
486
 
@@ -539,12 +545,12 @@ class DynamicModelChoiceMixin:
539
545
  field_name = getattr(self, "to_field_name") or "pk"
540
546
  filter_ = self.filter(field_name=field_name)
541
547
  try:
542
- self.queryset = filter_.filter(self.queryset, data)
543
- except (TypeError, ValidationError):
548
+ self.data_queryset = filter_.filter(self.queryset, data)
549
+ except (TypeError, ValueError, ValidationError):
544
550
  # Catch any error caused by invalid initial data passed from the user
545
- self.queryset = self.queryset.none()
551
+ self.data_queryset = self.queryset.none()
546
552
  else:
547
- self.queryset = self.queryset.none()
553
+ self.data_queryset = self.queryset.none()
548
554
 
549
555
  # Set the data URL on the APISelect widget (if not already set)
550
556
  widget = bound_field.field.widget
@@ -121,7 +121,7 @@ class BulkRenameForm(forms.Form):
121
121
  """
122
122
 
123
123
  find = forms.CharField()
124
- replace = forms.CharField()
124
+ replace = forms.CharField(required=False, strip=False)
125
125
  use_regex = forms.BooleanField(required=False, initial=True, label="Use regular expressions")
126
126
 
127
127
  def clean(self):
@@ -116,6 +116,23 @@ class ContentTypeSelect(StaticSelect2):
116
116
  option_template_name = "widgets/select_contenttype.html"
117
117
 
118
118
 
119
+ class MinimalModelChoiceIterator(ModelChoiceIterator):
120
+ """
121
+ Helper class for APISelect and APISelectMultiple.
122
+
123
+ Allows the widget to keep a full `queryset` for data validation, but, for performance reasons, returns a minimal
124
+ subset of choices at render time derived from the widget's `data_queryset`.
125
+ """
126
+
127
+ @property
128
+ def queryset(self):
129
+ return self.field.data_queryset
130
+
131
+ @queryset.setter
132
+ def queryset(self, value):
133
+ return self.field.data_queryset
134
+
135
+
119
136
  class APISelect(SelectWithDisabled):
120
137
  """
121
138
  A select widget populated via an API call
@@ -173,7 +190,7 @@ class APISelect(SelectWithDisabled):
173
190
  and isinstance(self.choices, Iterable)
174
191
  ):
175
192
 
176
- class ModelChoiceIteratorWithNullOption(ModelChoiceIterator):
193
+ class ModelChoiceIteratorWithNullOption(MinimalModelChoiceIterator):
177
194
  def __init__(self, *args, **kwargs):
178
195
  self.null_options = kwargs.pop("null_option", None)
179
196
  super().__init__(*args, **kwargs)
@@ -64,8 +64,8 @@ def generate_filter_resolver(schema_type, resolver_name, field_name):
64
64
  """
65
65
  filterset_class = schema_type._meta.filterset_class
66
66
 
67
- def resolve_filter(self, info, **kwargs):
68
- if not filterset_class or not kwargs:
67
+ def resolve_filter(self, *args, **kwargs):
68
+ if not filterset_class:
69
69
  return getattr(self, field_name).all()
70
70
 
71
71
  # Inverse of substitution logic from get_filtering_args_from_filterset() - transform "_type" back to "type"
@@ -42,7 +42,7 @@ from nautobot.dcim.graphql.types import (
42
42
  RearPortType,
43
43
  )
44
44
  from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipSideChoices
45
- from nautobot.extras.graphql.types import DynamicGroupType, TagType
45
+ from nautobot.extras.graphql.types import ContactAssociationType, DynamicGroupType, TagType
46
46
  from nautobot.extras.models import ComputedField, CustomField, Relationship
47
47
  from nautobot.extras.registry import registry
48
48
  from nautobot.extras.utils import check_if_key_is_graphql_safe
@@ -68,8 +68,9 @@ registry["graphql_types"]["dcim.powerport"] = PowerPortType
68
68
  registry["graphql_types"]["dcim.rack"] = RackType
69
69
  registry["graphql_types"]["dcim.rearport"] = RearPortType
70
70
  registry["graphql_types"]["dcim.location"] = LocationType
71
- registry["graphql_types"]["extras.tag"] = TagType
71
+ registry["graphql_types"]["extras.contactassociation"] = ContactAssociationType
72
72
  registry["graphql_types"]["extras.dynamicgroup"] = DynamicGroupType
73
+ registry["graphql_types"]["extras.tag"] = TagType
73
74
  registry["graphql_types"]["ipam.ipaddress"] = IPAddressType
74
75
  registry["graphql_types"]["ipam.prefix"] = PrefixType
75
76
  registry["graphql_types"]["ipam.vlan"] = VLANType
@@ -127,6 +128,11 @@ def extend_schema_type(schema_type):
127
128
  #
128
129
  schema_type = extend_schema_type_config_context(schema_type, model)
129
130
 
131
+ #
132
+ # Global features (contacts, teams, dynamic groups)
133
+ #
134
+ schema_type = extend_schema_type_global_features(schema_type, model)
135
+
130
136
  #
131
137
  # Relationships
132
138
  #
@@ -366,13 +372,37 @@ def extend_schema_type_config_context(schema_type, model):
366
372
  return schema_type
367
373
 
368
374
 
375
+ def extend_schema_type_global_features(schema_type, model):
376
+ """
377
+ Extend schema_type object to have attributes and resolvers for global features (contacts, dynamic groups, etc.).
378
+ """
379
+ if getattr(model, "is_contact_associable_model", False):
380
+
381
+ def resolve_associated_contacts(self, args):
382
+ return self.associated_contacts.all()
383
+
384
+ setattr(schema_type, "resolve_associated_contacts", resolve_associated_contacts)
385
+ schema_type._meta.fields["associated_contacts"] = graphene.Field.mounted(graphene.List(ContactAssociationType))
386
+
387
+ if getattr(model, "is_dynamic_group_associable_model", False):
388
+
389
+ def resolve_dynamic_groups(self, args):
390
+ return self.dynamic_groups
391
+
392
+ setattr(schema_type, "resolve_dynamic_groups", resolve_dynamic_groups)
393
+ schema_type._meta.fields["dynamic_groups"] = graphene.Field.mounted(graphene.List(DynamicGroupType))
394
+
395
+ return schema_type
396
+
397
+
369
398
  def extend_schema_type_relationships(schema_type, model):
370
399
  """Extend the schema type with attributes and resolvers corresponding
371
400
  to the relationships associated with this model."""
372
401
 
402
+ ct = ContentType.objects.get_for_model(model)
373
403
  relationships_by_side = {
374
- "source": Relationship.objects.get_for_model_source(model),
375
- "destination": Relationship.objects.get_for_model_destination(model),
404
+ "source": Relationship.objects.filter(source_type=ct),
405
+ "destination": Relationship.objects.filter(destination_type=ct),
376
406
  }
377
407
 
378
408
  prefix = ""
@@ -1,4 +1,3 @@
1
- import codecs
2
1
  import contextlib
3
2
  from io import BytesIO
4
3
 
@@ -15,6 +14,8 @@ from nautobot.core.api.renderers import NautobotCSVRenderer
15
14
  from nautobot.core.api.utils import get_serializer_for_model
16
15
  from nautobot.core.celery import app, register_jobs
17
16
  from nautobot.core.exceptions import AbortTransaction
17
+ from nautobot.core.jobs.cleanup import LogsCleanup
18
+ from nautobot.core.jobs.groups import RefreshDynamicGroupCaches
18
19
  from nautobot.core.utils.lookup import get_filterset_for_model
19
20
  from nautobot.core.utils.requests import get_filterable_params_from_filter_params
20
21
  from nautobot.extras.datasources import ensure_git_repository, git_repository_dry_run, refresh_datasource_content
@@ -37,6 +38,7 @@ class GitRepositorySync(Job):
37
38
 
38
39
  class Meta:
39
40
  name = "Git Repository: Sync"
41
+ description = "Clone and/or pull a Git repository, then refresh data sourced from this repository."
40
42
  has_sensitive_variables = False
41
43
 
42
44
  def run(self, repository):
@@ -66,6 +68,7 @@ class GitRepositoryDryRun(Job):
66
68
 
67
69
  class Meta:
68
70
  name = "Git Repository: Dry-Run"
71
+ description = "Dry run of Git repository sync - will not update data sourced from this repository."
69
72
  has_sensitive_variables = False
70
73
 
71
74
  def run(self, repository):
@@ -112,6 +115,7 @@ class ExportObjectList(Job):
112
115
 
113
116
  class Meta:
114
117
  name = "Export Object List"
118
+ description = "Export a list of objects to CSV or YAML, or render a specified Export Template."
115
119
  has_sensitive_variables = False
116
120
  # Exporting large querysets may take substantial processing time
117
121
  soft_time_limit = 1800
@@ -136,7 +140,15 @@ class ExportObjectList(Job):
136
140
  # such that they never are even seen here.
137
141
  query_params = QueryDict(query_string)
138
142
  self.logger.debug("Parsed query_params: `%s`", query_params.dict())
139
- default_non_filter_params = ("export", "page", "per_page", "sort")
143
+ default_non_filter_params = (
144
+ "all_filters_removed",
145
+ "export",
146
+ "page",
147
+ "per_page",
148
+ "saved_view",
149
+ "sort",
150
+ "table_changes_pending",
151
+ )
140
152
  filter_params = get_filterable_params_from_filter_params(
141
153
  query_params, default_non_filter_params, filterset_class()
142
154
  )
@@ -213,6 +225,7 @@ class ImportObjects(Job):
213
225
 
214
226
  class Meta:
215
227
  name = "Import Objects"
228
+ description = "Import objects from CSV-formatted data."
216
229
  has_sensitive_variables = False
217
230
  # Importing large files may take substantial processing time
218
231
  soft_time_limit = 1800
@@ -283,9 +296,7 @@ class ImportObjects(Job):
283
296
  if not csv_data and not csv_file:
284
297
  raise RunJobTaskFailed("Either csv_data or csv_file must be provided")
285
298
  if csv_file:
286
- # data_encoding is utf-8 and file_encoding is utf-8-sig
287
- # Bytes read from the original file are decoded according to file_encoding, and the result is encoded using data_encoding.
288
- csv_bytes = codecs.EncodedFile(csv_file, "utf-8", "utf-8-sig")
299
+ csv_bytes = csv_file
289
300
  else:
290
301
  csv_bytes = BytesIO(csv_data.encode("utf-8"))
291
302
 
@@ -317,5 +328,5 @@ class ImportObjects(Job):
317
328
  raise RunJobTaskFailed("CSV import not fully successful, see logs")
318
329
 
319
330
 
320
- jobs = [ExportObjectList, GitRepositorySync, GitRepositoryDryRun, ImportObjects]
331
+ jobs = [ExportObjectList, GitRepositorySync, GitRepositoryDryRun, ImportObjects, LogsCleanup, RefreshDynamicGroupCaches]
321
332
  register_jobs(*jobs)
@@ -0,0 +1,100 @@
1
+ from datetime import timedelta
2
+
3
+ from django.core.exceptions import PermissionDenied
4
+ from django.db.models.signals import pre_delete
5
+ from django.utils import timezone
6
+
7
+ from nautobot.core.choices import ChoiceSet
8
+ from nautobot.core.utils.config import get_settings_or_config
9
+ from nautobot.extras.jobs import IntegerVar, Job, MultiChoiceVar
10
+ from nautobot.extras.models import JobResult, ObjectChange
11
+ from nautobot.extras.signals import _handle_deleted_object
12
+
13
+ name = "System Jobs"
14
+
15
+
16
+ class CleanupTypes(ChoiceSet):
17
+ JOB_RESULT = "extras.JobResult"
18
+ OBJECT_CHANGE = "extras.ObjectChange"
19
+
20
+ CHOICES = (
21
+ (JOB_RESULT, "Job results"),
22
+ (OBJECT_CHANGE, "Change logs"),
23
+ )
24
+
25
+
26
+ class LogsCleanup(Job):
27
+ """
28
+ System job to clean up ObjectChange and/or JobResult (and JobLogEntry) records older than a given age.
29
+ """
30
+
31
+ cleanup_types = MultiChoiceVar(
32
+ choices=CleanupTypes.CHOICES,
33
+ required=True,
34
+ )
35
+
36
+ max_age = IntegerVar(
37
+ description=(
38
+ "Maximum age of records to retain, in days. "
39
+ "Leave empty to use the CHANGELOG_RETENTION setting as the maximum."
40
+ ),
41
+ label="Max Age",
42
+ min_value=0,
43
+ required=False,
44
+ )
45
+
46
+ class Meta:
47
+ name = "Logs Cleanup"
48
+ description = "Delete ObjectChange and/or JobResult/JobLogEntry records older than a specified cutoff."
49
+ has_sensitive_variables = False
50
+
51
+ def run(self, *, cleanup_types, max_age=None):
52
+ if max_age in (None, ""):
53
+ max_age = get_settings_or_config("CHANGELOG_RETENTION")
54
+ if max_age == 0:
55
+ self.logger.warning(
56
+ "CHANGELOG_RETENTION setting is set to zero, disabling this Job. "
57
+ "If you wish to use this Job to delete records, you must specify a `max_age` value."
58
+ )
59
+ return 0
60
+
61
+ if CleanupTypes.JOB_RESULT in cleanup_types and not self.user.has_perm("extras.delete_jobresult"):
62
+ self.logger.error('User "%s" does not have permission to delete JobResult records', self.user)
63
+ raise PermissionDenied("User does not have delete permissions for JobResult records")
64
+
65
+ if CleanupTypes.OBJECT_CHANGE in cleanup_types and not self.user.has_perm("extras.delete_objectchange"):
66
+ self.logger.error('User "%s" does not have permission to delete ObjectChange records', self.user)
67
+ raise PermissionDenied("User does not have delete permissions for ObjectChange records")
68
+
69
+ # Bulk delete goes much faster if Django doesn't have signals to process.
70
+ # Temporarily detach the ones we *know* to be irrelevant.
71
+ self.logger.debug("Temporarily disconnecting some signals for performance")
72
+ pre_delete.disconnect(_handle_deleted_object)
73
+
74
+ try:
75
+ cutoff = timezone.now() - timedelta(days=max_age)
76
+ result = {}
77
+
78
+ if CleanupTypes.JOB_RESULT in cleanup_types:
79
+ self.logger.info("Deleting JobResult records prior to %s", cutoff)
80
+ _, deleted_dict = JobResult.objects.restrict(self.user, "delete").filter(date_done__lt=cutoff).delete()
81
+ result.setdefault("extras.JobResult", 0)
82
+ result.setdefault("extras.JobLogEntry", 0)
83
+ result.update(**deleted_dict)
84
+ self.logger.info(
85
+ "Deleted %d JobResult records and their associated %d JobLogEntry records",
86
+ result["extras.JobResult"],
87
+ result["extras.JobLogEntry"],
88
+ )
89
+
90
+ if CleanupTypes.OBJECT_CHANGE in cleanup_types:
91
+ self.logger.info("Deleting ObjectChange records prior to %s", cutoff)
92
+ deleted_count, _ = ObjectChange.objects.restrict(self.user, "delete").filter(time__lt=cutoff).delete()
93
+ self.logger.info("Deleted %d ObjectChange records", deleted_count)
94
+ result["extras.ObjectChange"] = deleted_count
95
+
96
+ return result
97
+ finally:
98
+ # Be sure to clean up after ourselves!
99
+ self.logger.debug("Re-connecting signals")
100
+ pre_delete.connect(_handle_deleted_object)
@@ -0,0 +1,38 @@
1
+ from nautobot.extras.choices import DynamicGroupTypeChoices
2
+ from nautobot.extras.jobs import Job, ObjectVar
3
+ from nautobot.extras.models import DynamicGroup
4
+
5
+ name = "System Jobs"
6
+
7
+
8
+ class RefreshDynamicGroupCaches(Job):
9
+ """
10
+ System job to recalculate and re-cache the members of Dynamic Groups for improved performance.
11
+ """
12
+
13
+ single_group = ObjectVar(
14
+ description="Select to refresh only a single specified group instead of all groups",
15
+ model=DynamicGroup,
16
+ query_params={
17
+ "group_type": [DynamicGroupTypeChoices.TYPE_DYNAMIC_FILTER, DynamicGroupTypeChoices.TYPE_DYNAMIC_SET],
18
+ },
19
+ required=False,
20
+ )
21
+
22
+ class Meta:
23
+ name = "Refresh Dynamic Group Caches"
24
+ description = "Re-calculate and re-cache the membership lists of Dynamic Groups."
25
+ has_sensitive_variables = False
26
+
27
+ def run(self, single_group=None):
28
+ groups = DynamicGroup.objects.restrict(self.user, "view").exclude(
29
+ group_type=DynamicGroupTypeChoices.TYPE_STATIC
30
+ )
31
+ if single_group is not None:
32
+ groups = groups.filter(pk=single_group.pk)
33
+
34
+ for group in groups:
35
+ group.update_cached_members()
36
+ self.logger.info("Cache refreshed successfully, now with %d members", group.count, extra={"object": group})
37
+
38
+ self.logger.info("Cache(s) refreshed")