nautobot 2.4.10__py3-none-any.whl → 2.4.12__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 (439) hide show
  1. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -114
  2. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +1 -1
  3. nautobot/circuits/views.py +76 -6
  4. nautobot/cloud/navigation.py +1 -1
  5. nautobot/cloud/tests/test_views.py +13 -1
  6. nautobot/cloud/views.py +39 -9
  7. nautobot/core/celery/__init__.py +21 -0
  8. nautobot/core/celery/encoders.py +3 -0
  9. nautobot/core/forms/forms.py +4 -1
  10. nautobot/core/jobs/bulk_actions.py +8 -8
  11. nautobot/core/jobs/cleanup.py +11 -0
  12. nautobot/core/management/commands/generate_test_data.py +2 -1
  13. nautobot/core/templates/components/panel/header_extra_content_table.html +12 -1
  14. nautobot/core/templates/components/panel/panel.html +4 -0
  15. nautobot/core/templates/generic/object_retrieve.html +2 -1
  16. nautobot/core/testing/mixins.py +19 -1
  17. nautobot/core/testing/views.py +104 -8
  18. nautobot/core/tests/test_jobs.py +20 -4
  19. nautobot/core/tests/test_utils.py +17 -0
  20. nautobot/core/tests/test_views.py +2 -2
  21. nautobot/core/tests/test_views_utils.py +53 -2
  22. nautobot/core/ui/object_detail.py +5 -1
  23. nautobot/core/utils/lookup.py +4 -2
  24. nautobot/core/utils/module_loading.py +23 -0
  25. nautobot/core/views/generic.py +2 -12
  26. nautobot/core/views/mixins.py +19 -1
  27. nautobot/core/views/renderers.py +4 -13
  28. nautobot/core/views/utils.py +16 -0
  29. nautobot/dcim/api/serializers.py +13 -0
  30. nautobot/dcim/api/urls.py +1 -0
  31. nautobot/dcim/api/views.py +20 -0
  32. nautobot/dcim/apps.py +1 -0
  33. nautobot/dcim/factory.py +11 -0
  34. nautobot/dcim/filters/__init__.py +116 -0
  35. nautobot/dcim/filters/mixins.py +2 -1
  36. nautobot/dcim/forms.py +205 -19
  37. nautobot/dcim/migrations/0070_modulefamily_models.py +92 -0
  38. nautobot/dcim/models/__init__.py +2 -0
  39. nautobot/dcim/models/device_component_templates.py +14 -0
  40. nautobot/dcim/models/device_components.py +13 -1
  41. nautobot/dcim/models/devices.py +72 -0
  42. nautobot/dcim/navigation.py +16 -0
  43. nautobot/dcim/tables/__init__.py +2 -0
  44. nautobot/dcim/tables/devices.py +50 -0
  45. nautobot/dcim/tables/devicetypes.py +35 -1
  46. nautobot/dcim/tables/template_code.py +2 -0
  47. nautobot/dcim/templates/dcim/controller/base.html +1 -9
  48. nautobot/dcim/templates/dcim/controller_retrieve.html +2 -83
  49. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +2 -25
  50. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -90
  51. nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
  52. nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +1 -63
  53. nautobot/dcim/templates/dcim/location.html +2 -249
  54. nautobot/dcim/templates/dcim/location_edit.html +2 -38
  55. nautobot/dcim/templates/dcim/location_retrieve.html +249 -0
  56. nautobot/dcim/templates/dcim/location_update.html +38 -0
  57. nautobot/dcim/templates/dcim/module_update.html +1 -0
  58. nautobot/dcim/templates/dcim/modulebay_retrieve.html +93 -1
  59. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +31 -0
  60. nautobot/dcim/templates/dcim/moduletype_retrieve.html +6 -0
  61. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -160
  62. nautobot/dcim/templates/dcim/virtualchassis.html +2 -51
  63. nautobot/dcim/templates/dcim/virtualchassis_add.html +2 -22
  64. nautobot/dcim/templates/dcim/virtualchassis_create.html +22 -0
  65. nautobot/dcim/templates/dcim/virtualchassis_edit.html +2 -93
  66. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +51 -0
  67. nautobot/dcim/templates/dcim/virtualchassis_update.html +93 -0
  68. nautobot/dcim/tests/test_api.py +35 -0
  69. nautobot/dcim/tests/test_filters.py +102 -3
  70. nautobot/dcim/tests/test_models.py +146 -0
  71. nautobot/dcim/tests/test_views.py +70 -97
  72. nautobot/dcim/urls.py +5 -80
  73. nautobot/dcim/views.py +584 -342
  74. nautobot/extras/api/views.py +9 -2
  75. nautobot/extras/datasources/git.py +9 -1
  76. nautobot/extras/forms/forms.py +9 -5
  77. nautobot/extras/jobs.py +4 -2
  78. nautobot/extras/jobs_ui.py +208 -0
  79. nautobot/extras/models/datasources.py +5 -8
  80. nautobot/extras/models/jobs.py +5 -0
  81. nautobot/extras/models/tags.py +4 -0
  82. nautobot/extras/plugins/__init__.py +3 -0
  83. nautobot/extras/tables.py +40 -3
  84. nautobot/extras/templates/extras/configcontext.html +2 -220
  85. nautobot/extras/templates/extras/configcontext_edit.html +2 -50
  86. nautobot/extras/templates/extras/configcontext_retrieve.html +2 -0
  87. nautobot/extras/templates/extras/configcontext_update.html +50 -0
  88. nautobot/extras/templates/extras/configcontextschema.html +2 -48
  89. nautobot/extras/templates/extras/configcontextschema_edit.html +2 -19
  90. nautobot/extras/templates/extras/configcontextschema_retrieve.html +48 -0
  91. nautobot/extras/templates/extras/configcontextschema_update.html +19 -0
  92. nautobot/extras/templates/extras/inc/configcontext_data.html +1 -0
  93. nautobot/extras/templates/extras/inc/configcontext_format.html +6 -0
  94. nautobot/extras/templates/extras/job_detail.html +1 -326
  95. nautobot/extras/templates/extras/job_edit.html +12 -6
  96. nautobot/extras/templates/extras/tag.html +2 -52
  97. nautobot/extras/templates/extras/tag_edit.html +2 -15
  98. nautobot/extras/templates/extras/tag_retrieve.html +2 -0
  99. nautobot/extras/templates/extras/tag_update.html +15 -0
  100. nautobot/extras/templates/extras/team_retrieve.html +2 -2
  101. nautobot/extras/tests/test_api.py +15 -15
  102. nautobot/extras/tests/test_filters.py +4 -4
  103. nautobot/extras/tests/test_jobs.py +23 -10
  104. nautobot/extras/tests/test_models.py +19 -8
  105. nautobot/extras/tests/test_plugins.py +6 -3
  106. nautobot/extras/tests/test_views.py +66 -11
  107. nautobot/extras/urls.py +4 -134
  108. nautobot/extras/views.py +186 -178
  109. nautobot/ipam/models.py +19 -4
  110. nautobot/ipam/tables.py +19 -0
  111. nautobot/ipam/templates/ipam/vlan.html +2 -84
  112. nautobot/ipam/templates/ipam/vlan_edit.html +2 -24
  113. nautobot/ipam/templates/ipam/vlan_retrieve.html +84 -0
  114. nautobot/ipam/templates/ipam/vlan_update.html +24 -0
  115. nautobot/ipam/tests/test_views.py +5 -0
  116. nautobot/ipam/urls.py +1 -21
  117. nautobot/ipam/views.py +45 -70
  118. nautobot/project-static/docs/404.html +33 -10
  119. nautobot/project-static/docs/apps/index.html +33 -10
  120. nautobot/project-static/docs/apps/nautobot-apps.html +33 -10
  121. nautobot/project-static/docs/assets/javascripts/{bundle.13a4f30d.min.js → bundle.56ea9cef.min.js} +2 -2
  122. nautobot/project-static/docs/assets/javascripts/{bundle.13a4f30d.min.js.map → bundle.56ea9cef.min.js.map} +2 -2
  123. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +33 -10
  124. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +33 -10
  125. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +33 -10
  126. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +33 -10
  127. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +33 -10
  128. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +33 -10
  129. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +33 -10
  130. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +33 -10
  131. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +33 -10
  132. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +33 -10
  133. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +33 -10
  134. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +33 -10
  135. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +33 -10
  136. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +33 -10
  137. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +33 -10
  138. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +33 -10
  139. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +33 -10
  140. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +33 -10
  141. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +33 -10
  142. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +122 -10
  143. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +33 -10
  144. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +33 -10
  145. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +33 -10
  146. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +33 -10
  147. nautobot/project-static/docs/development/apps/api/configuration-view.html +33 -10
  148. nautobot/project-static/docs/development/apps/api/database-backend-config.html +33 -10
  149. nautobot/project-static/docs/development/apps/api/models/django-admin.html +33 -10
  150. nautobot/project-static/docs/development/apps/api/models/global-search.html +33 -10
  151. nautobot/project-static/docs/development/apps/api/models/graphql.html +33 -10
  152. nautobot/project-static/docs/development/apps/api/models/index.html +33 -10
  153. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +42 -10
  154. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +33 -10
  155. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +33 -10
  156. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +33 -10
  157. nautobot/project-static/docs/development/apps/api/platform-features/index.html +33 -10
  158. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +33 -10
  159. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +33 -10
  160. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +33 -10
  161. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +34 -11
  162. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +33 -10
  163. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +33 -10
  164. nautobot/project-static/docs/development/apps/api/prometheus.html +33 -10
  165. nautobot/project-static/docs/development/apps/api/setup.html +33 -10
  166. nautobot/project-static/docs/development/apps/api/testing.html +33 -10
  167. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +33 -10
  168. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +33 -10
  169. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +33 -10
  170. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +33 -10
  171. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +33 -10
  172. nautobot/project-static/docs/development/apps/api/views/base-template.html +33 -10
  173. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +33 -10
  174. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +33 -10
  175. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +33 -10
  176. nautobot/project-static/docs/development/apps/api/views/index.html +33 -10
  177. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +33 -10
  178. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +33 -10
  179. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +33 -10
  180. nautobot/project-static/docs/development/apps/api/views/notes.html +33 -10
  181. nautobot/project-static/docs/development/apps/api/views/rest-api.html +33 -10
  182. nautobot/project-static/docs/development/apps/api/views/urls.html +33 -10
  183. nautobot/project-static/docs/development/apps/index.html +33 -10
  184. nautobot/project-static/docs/development/apps/migration/code-updates.html +33 -10
  185. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +33 -10
  186. nautobot/project-static/docs/development/apps/migration/from-v1.html +33 -10
  187. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +33 -10
  188. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +33 -10
  189. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +33 -10
  190. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +33 -10
  191. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +33 -10
  192. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +33 -10
  193. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +33 -10
  194. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +33 -10
  195. nautobot/project-static/docs/development/apps/porting-from-netbox.html +33 -10
  196. nautobot/project-static/docs/development/core/application-registry.html +33 -10
  197. nautobot/project-static/docs/development/core/best-practices.html +33 -10
  198. nautobot/project-static/docs/development/core/bootstrap-ui.html +33 -10
  199. nautobot/project-static/docs/development/core/caching.html +33 -10
  200. nautobot/project-static/docs/development/core/controllers.html +33 -10
  201. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +33 -10
  202. nautobot/project-static/docs/development/core/generic-views.html +33 -10
  203. nautobot/project-static/docs/development/core/getting-started.html +33 -10
  204. nautobot/project-static/docs/development/core/homepage.html +33 -10
  205. nautobot/project-static/docs/development/core/index.html +33 -10
  206. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +33 -10
  207. nautobot/project-static/docs/development/core/model-checklist.html +33 -10
  208. nautobot/project-static/docs/development/core/model-features.html +33 -10
  209. nautobot/project-static/docs/development/core/natural-keys.html +33 -10
  210. nautobot/project-static/docs/development/core/navigation-menu.html +33 -10
  211. nautobot/project-static/docs/development/core/release-checklist.html +33 -10
  212. nautobot/project-static/docs/development/core/role-internals.html +33 -10
  213. nautobot/project-static/docs/development/core/settings.html +33 -10
  214. nautobot/project-static/docs/development/core/style-guide.html +33 -10
  215. nautobot/project-static/docs/development/core/templates.html +33 -10
  216. nautobot/project-static/docs/development/core/testing.html +33 -10
  217. nautobot/project-static/docs/development/core/ui-component-framework.html +33 -10
  218. nautobot/project-static/docs/development/core/user-preferences.html +33 -10
  219. nautobot/project-static/docs/development/index.html +33 -10
  220. nautobot/project-static/docs/development/jobs/getting-started.html +33 -10
  221. nautobot/project-static/docs/development/jobs/index.html +33 -10
  222. nautobot/project-static/docs/development/jobs/installation.html +33 -10
  223. nautobot/project-static/docs/development/jobs/job-extensions.html +33 -10
  224. nautobot/project-static/docs/development/jobs/job-logging.html +33 -10
  225. nautobot/project-static/docs/development/jobs/job-patterns.html +33 -10
  226. nautobot/project-static/docs/development/jobs/job-structure.html +33 -10
  227. nautobot/project-static/docs/development/jobs/migration/from-v1.html +33 -10
  228. nautobot/project-static/docs/development/jobs/testing.html +33 -10
  229. nautobot/project-static/docs/index.html +33 -10
  230. nautobot/project-static/docs/insert-analytics.sh +36 -0
  231. nautobot/project-static/docs/objects.inv +0 -0
  232. nautobot/project-static/docs/overview/application_stack.html +33 -10
  233. nautobot/project-static/docs/overview/design_philosophy.html +33 -10
  234. nautobot/project-static/docs/release-notes/index.html +33 -10
  235. nautobot/project-static/docs/release-notes/version-1.0.html +33 -10
  236. nautobot/project-static/docs/release-notes/version-1.1.html +33 -10
  237. nautobot/project-static/docs/release-notes/version-1.2.html +33 -10
  238. nautobot/project-static/docs/release-notes/version-1.3.html +33 -10
  239. nautobot/project-static/docs/release-notes/version-1.4.html +33 -10
  240. nautobot/project-static/docs/release-notes/version-1.5.html +33 -10
  241. nautobot/project-static/docs/release-notes/version-1.6.html +33 -10
  242. nautobot/project-static/docs/release-notes/version-2.0.html +33 -10
  243. nautobot/project-static/docs/release-notes/version-2.1.html +33 -10
  244. nautobot/project-static/docs/release-notes/version-2.2.html +33 -10
  245. nautobot/project-static/docs/release-notes/version-2.3.html +33 -10
  246. nautobot/project-static/docs/release-notes/version-2.4.html +363 -10
  247. nautobot/project-static/docs/requirements.txt +1 -1
  248. nautobot/project-static/docs/search/search_index.json +1 -1
  249. nautobot/project-static/docs/sitemap.xml +302 -298
  250. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  251. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +33 -10
  252. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +33 -10
  253. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +33 -10
  254. nautobot/project-static/docs/user-guide/administration/configuration/index.html +33 -10
  255. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +33 -10
  256. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +33 -10
  257. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +33 -10
  258. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +33 -10
  259. nautobot/project-static/docs/user-guide/administration/guides/docker.html +33 -10
  260. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +33 -10
  261. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +33 -10
  262. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +33 -10
  263. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +33 -10
  264. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +33 -10
  265. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +33 -10
  266. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +33 -10
  267. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +33 -10
  268. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +33 -10
  269. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +33 -10
  270. nautobot/project-static/docs/user-guide/administration/installation/index.html +33 -10
  271. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +33 -10
  272. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +33 -10
  273. nautobot/project-static/docs/user-guide/administration/installation/services.html +33 -10
  274. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +33 -10
  275. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +33 -10
  276. nautobot/project-static/docs/user-guide/administration/security/index.html +33 -10
  277. nautobot/project-static/docs/user-guide/administration/security/notices.html +33 -10
  278. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +33 -10
  279. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +33 -10
  280. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +33 -10
  281. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +33 -10
  282. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +33 -10
  283. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +33 -10
  284. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +33 -10
  285. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +33 -10
  286. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +33 -10
  287. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +33 -10
  288. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +33 -10
  289. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +33 -10
  290. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +33 -10
  291. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +33 -10
  292. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +33 -10
  293. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +33 -10
  294. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +33 -10
  295. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +33 -10
  296. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +33 -10
  297. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +33 -10
  298. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +33 -10
  299. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +33 -10
  300. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +33 -10
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +33 -10
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +33 -10
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +33 -10
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +33 -10
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +33 -10
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +33 -10
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +33 -10
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +33 -10
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +33 -10
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +33 -10
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +33 -10
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +45 -22
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +33 -10
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +33 -10
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +33 -10
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +33 -10
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +33 -10
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +33 -10
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +33 -10
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +33 -10
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +33 -10
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +33 -10
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +37 -10
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +37 -10
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +37 -10
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +10261 -0
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +36 -13
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +33 -10
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +33 -10
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +33 -10
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +33 -10
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +33 -10
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +33 -10
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +33 -10
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +33 -10
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +33 -10
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +33 -10
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +33 -10
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +33 -10
  340. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +33 -10
  341. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +33 -10
  342. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +33 -10
  343. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +33 -10
  344. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +33 -10
  345. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +33 -10
  346. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +33 -10
  347. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +33 -10
  348. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +33 -10
  349. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +33 -10
  350. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +33 -10
  351. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +33 -10
  352. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +33 -10
  353. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +33 -10
  354. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +33 -10
  355. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +33 -10
  356. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +33 -10
  357. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +33 -10
  358. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +33 -10
  359. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +33 -10
  360. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +33 -10
  361. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +33 -10
  362. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +33 -10
  363. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +33 -10
  364. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +33 -10
  365. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +33 -10
  366. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +33 -10
  367. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +33 -10
  368. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +33 -10
  369. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +33 -10
  370. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +33 -10
  371. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +33 -10
  372. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +33 -10
  373. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +33 -10
  374. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +33 -10
  375. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +33 -10
  376. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +33 -10
  377. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +33 -10
  378. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +33 -10
  379. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +33 -10
  380. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +44 -18
  381. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +33 -10
  382. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +33 -10
  383. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +33 -10
  384. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +33 -10
  385. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +33 -10
  386. nautobot/project-static/docs/user-guide/index.html +33 -10
  387. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +33 -10
  388. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +33 -10
  389. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +33 -10
  390. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +33 -10
  391. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +33 -10
  392. nautobot/project-static/docs/user-guide/platform-functionality/events.html +33 -10
  393. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +33 -10
  394. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +33 -10
  395. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +39 -11
  396. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +33 -10
  397. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +33 -10
  398. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +33 -10
  399. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +33 -10
  400. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +33 -10
  401. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +33 -10
  402. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +33 -10
  403. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +33 -10
  404. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +33 -10
  405. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +33 -10
  406. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +33 -10
  407. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +33 -10
  408. nautobot/project-static/docs/user-guide/platform-functionality/note.html +33 -10
  409. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +33 -10
  410. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +33 -10
  411. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +33 -10
  412. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +33 -10
  413. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +33 -10
  414. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +33 -10
  415. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +33 -10
  416. nautobot/project-static/docs/user-guide/platform-functionality/role.html +33 -10
  417. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +33 -10
  418. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +33 -10
  419. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +33 -10
  420. nautobot/project-static/docs/user-guide/platform-functionality/status.html +33 -10
  421. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +33 -10
  422. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +33 -10
  423. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +33 -10
  424. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +33 -10
  425. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +33 -10
  426. nautobot/tenancy/tables.py +2 -0
  427. nautobot/virtualization/tests/test_views.py +1 -1
  428. nautobot/wireless/forms.py +0 -1
  429. nautobot/wireless/models.py +1 -1
  430. nautobot/wireless/navigation.py +1 -1
  431. nautobot/wireless/tables.py +18 -3
  432. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/METADATA +4 -4
  433. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/RECORD +439 -419
  434. /nautobot/dcim/templates/dcim/{platform_edit.html → platform_create.html} +0 -0
  435. /nautobot/extras/test_jobs/{pass.py → pass_job.py} +0 -0
  436. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/LICENSE.txt +0 -0
  437. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/NOTICE +0 -0
  438. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/WHEEL +0 -0
  439. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/entry_points.txt +0 -0
@@ -512,12 +512,19 @@ class JobViewSetBase(
512
512
  serializer_class = serializers.JobSerializer
513
513
  filterset_class = filters.JobFilterSet
514
514
 
515
+ def get_object(self):
516
+ """Get the Job instance and reload the job class to ensure we have the latest version of the job code."""
517
+ obj = super().get_object()
518
+ get_job(obj.class_path, reload=True)
519
+
520
+ return obj
521
+
515
522
  @extend_schema(responses={"200": serializers.JobVariableSerializer(many=True)})
516
523
  @action(detail=True, filterset_class=None)
517
524
  def variables(self, request, *args, **kwargs):
518
525
  """Get details of the input variables that may/must be specified to run a particular Job."""
519
526
  job_model = self.get_object()
520
- job_class = get_job(job_model.class_path, reload=True)
527
+ job_class = job_model.job_class
521
528
  if job_class is None:
522
529
  raise Http404
523
530
  variables_dict = job_class._get_vars()
@@ -599,7 +606,7 @@ class JobViewSetBase(
599
606
  "One of these two flags must be removed before this job can be scheduled or run."
600
607
  )
601
608
 
602
- job_class = get_job(job_model.class_path, reload=True)
609
+ job_class = job_model.job_class
603
610
  if job_class is None:
604
611
  raise MethodNotAllowed(
605
612
  request.method, detail="This job's source code could not be located and cannot be run"
@@ -17,7 +17,7 @@ from git import InvalidGitRepositoryError, Repo
17
17
  import yaml
18
18
 
19
19
  from nautobot.core.utils.git import GitRepo
20
- from nautobot.core.utils.module_loading import import_modules_privately
20
+ from nautobot.core.utils.module_loading import check_name_safe_to_import_privately, import_modules_privately
21
21
  from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
22
22
  from nautobot.extras.choices import (
23
23
  LogLevelChoices,
@@ -725,6 +725,14 @@ def refresh_job_code_from_repository(repository_slug, skip_reimport=False, ignor
725
725
  ignore_import_errors (bool): If True, any exceptions raised in the import will be caught and logged.
726
726
  If False, exceptions will be re-raised after logging.
727
727
  """
728
+ # Enforced during GitRepository.clean() but just in case someone created a bad record without cleaning:
729
+ permitted, reason = check_name_safe_to_import_privately(repository_slug)
730
+ if not permitted:
731
+ logger.error("The repository_slug %r is invalid as it is %s", repository_slug, reason)
732
+ if ignore_import_errors:
733
+ return
734
+ raise ValueError(f"The repository_slug {repository_slug!r} is invalid as it is {reason}")
735
+
728
736
  # Unload any previous version of this module and its submodules if present
729
737
  for job_class_path in list(registry["jobs"]):
730
738
  if job_class_path.startswith(f"{repository_slug}."):
@@ -914,13 +914,13 @@ class ExternalIntegrationBulkEditForm(NautobotBulkEditForm):
914
914
  secrets_group = DynamicModelChoiceField(required=False, queryset=SecretsGroup.objects.all())
915
915
  verify_ssl = forms.NullBooleanField(required=False, label="Verify SSL", widget=BulkEditNullBooleanSelect)
916
916
  timeout = forms.IntegerField(required=False, min_value=0)
917
- extra_config = forms.JSONField(required=False)
917
+ extra_config = JSONField(required=False, widget=forms.Textarea, help_text="JSON data")
918
918
  http_method = forms.ChoiceField(
919
919
  required=False,
920
920
  label="HTTP Method",
921
921
  choices=add_blank_choice(WebhookHttpMethodChoices),
922
922
  )
923
- headers = forms.JSONField(required=False, label="HTTP Request headers")
923
+ headers = JSONField(required=False, widget=forms.Textarea, help_text="Headers for the HTTP request")
924
924
 
925
925
  class Meta:
926
926
  model = ExternalIntegration
@@ -1487,7 +1487,6 @@ class JobQueueBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
1487
1487
  class Meta:
1488
1488
  model = JobQueue
1489
1489
  nullable_fields = [
1490
- "secrets_group",
1491
1490
  "description",
1492
1491
  "tenant",
1493
1492
  ]
@@ -1910,8 +1909,8 @@ class RelationshipBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditFormMixin
1910
1909
  )
1911
1910
  source_hidden = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
1912
1911
  destination_hidden = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
1913
- source_filter = forms.JSONField(required=False, widget=forms.Textarea, help_text="Filter for the source")
1914
- destination_filter = forms.JSONField(required=False, widget=forms.Textarea, help_text="Filter for the destination")
1912
+ source_filter = JSONField(required=False, widget=forms.Textarea, help_text="Filter for the source")
1913
+ destination_filter = JSONField(required=False, widget=forms.Textarea, help_text="Filter for the destination")
1915
1914
  source_type = CSVContentTypeField(
1916
1915
  queryset=ContentType.objects.filter(FeatureQuery("relationships").get_query()), required=False
1917
1916
  )
@@ -2256,6 +2255,8 @@ class WebhookBulkEditForm(BootstrapMixin, NoteModelBulkEditFormMixin):
2256
2255
  secret = forms.CharField(required=False, max_length=CHARFIELD_MAX_LENGTH)
2257
2256
  ca_file_path = forms.CharField(required=False, max_length=4096)
2258
2257
  http_content_type = forms.CharField(required=False, max_length=CHARFIELD_MAX_LENGTH)
2258
+ additional_headers = forms.CharField(required=False, widget=forms.Textarea)
2259
+ body_template = forms.CharField(required=False, widget=forms.Textarea)
2259
2260
 
2260
2261
  # Choice field
2261
2262
  http_method = forms.ChoiceField(
@@ -2279,6 +2280,8 @@ class WebhookBulkEditForm(BootstrapMixin, NoteModelBulkEditFormMixin):
2279
2280
  "type_delete",
2280
2281
  "http_method",
2281
2282
  "http_content_type",
2283
+ "additional_headers",
2284
+ "body_template",
2282
2285
  "ssl_verification",
2283
2286
  "ca_file_path",
2284
2287
  "payload_url",
@@ -2286,6 +2289,7 @@ class WebhookBulkEditForm(BootstrapMixin, NoteModelBulkEditFormMixin):
2286
2289
  "add_content_types",
2287
2290
  "remove_content_types",
2288
2291
  )
2292
+ nullable_fields = ("additional_headers",)
2289
2293
 
2290
2294
 
2291
2295
  class WebhookForm(BootstrapMixin, forms.ModelForm):
nautobot/extras/jobs.py CHANGED
@@ -17,6 +17,7 @@ from celery.exceptions import Ignore, Reject
17
17
  from celery.utils.log import get_task_logger
18
18
  from db_file_storage.form_widgets import DBClearableFileInput
19
19
  from django import forms
20
+ from django.apps import apps
20
21
  from django.conf import settings
21
22
  from django.contrib.auth import get_user_model
22
23
  from django.core.cache import cache
@@ -1142,8 +1143,9 @@ def get_job(class_path, reload=False):
1142
1143
  # System job - not reloadable
1143
1144
  reload = False
1144
1145
  if any(class_path.startswith(f"{app_name}.") for app_name in settings.PLUGINS):
1145
- # App provided job - not reloadable
1146
- reload = False
1146
+ # App provided job - only reload if the app provides dynamic jobs
1147
+ app_config = apps.get_app_config(class_path.split(".")[0])
1148
+ reload = getattr(app_config, "provides_dynamic_jobs", False)
1147
1149
  jobs = get_jobs(reload=reload)
1148
1150
  return jobs.get(class_path, None)
1149
1151
 
@@ -0,0 +1,208 @@
1
+ from django.template import Context
2
+ from django.template.loader import render_to_string
3
+ from django.utils.html import format_html, format_html_join
4
+ from django.utils.safestring import mark_safe
5
+
6
+ from nautobot.core.templatetags import helpers
7
+ from nautobot.core.ui.object_detail import Button, KeyValueTablePanel, ObjectFieldsPanel
8
+ from nautobot.core.views.utils import get_obj_from_context
9
+
10
+
11
+ class JobRunScheduleButton(Button):
12
+ """
13
+ A custom button for running or scheduling a job.
14
+
15
+ This button is rendered only if the user has the 'extras.run_job' permission.
16
+ It also disables itself (via HTML 'disabled' attribute) if the related object is not
17
+ installed or not enabled.
18
+ """
19
+
20
+ def get_extra_context(self, context):
21
+ """Inject dynamic attributes (e.g. 'disabled') based on object state into the rendering context."""
22
+ extra_context = super().get_extra_context(context)
23
+ obj = context.get("object")
24
+ if not obj.installed or not obj.enabled:
25
+ if extra_context["attributes"] is None:
26
+ extra_context["attributes"] = {}
27
+ extra_context["attributes"]["disabled"] = "disabled"
28
+ return extra_context
29
+
30
+
31
+ class JobKeyValueOverrideValueTablePanel(KeyValueTablePanel):
32
+ """A table panel for displaying key-value pairs of job-related attributes, along with any override values defined on the job object."""
33
+
34
+ def _render_overridden_text(self, text):
35
+ """
36
+ Render a simple overridden value inside a muted <span>, with a customizable prefix.
37
+
38
+ Args:
39
+ value (str): The content to display.
40
+ prefix (str): The label shown before the value (default: 'default is').
41
+ """
42
+ return format_html('<span class="text-muted">overridden; default is {}</span>', mark_safe(text)) # noqa: S308
43
+
44
+ def _render_overridden_block(self, content):
45
+ """
46
+ Render a more complex block of HTML content (like rendered markdown or JSON)
47
+ in a div with a muted label indicating it's an overridden value.
48
+ """
49
+ return format_html('<div class="text-muted">overridden; default is:<br>{}</div>', content)
50
+
51
+ def render_description_default(self, default_value):
52
+ """
53
+ Render a description field's default value as markdown.
54
+
55
+ If the default value is a string, it is rendered as markdown;
56
+ otherwise, a placeholder is shown.
57
+ """
58
+ rendered = (
59
+ helpers.render_markdown(default_value)
60
+ if isinstance(default_value, str)
61
+ else helpers.placeholder(default_value)
62
+ )
63
+ return self._render_overridden_block(rendered)
64
+
65
+ def render_time_limit_default(self, default_value, system_default_value, override=True):
66
+ """
67
+ Render a time limit value, falling back to the system default if needed.
68
+ """
69
+ message = "{} seconds" if default_value > 0 else "{} seconds (system default)"
70
+ value = default_value if default_value > 0 else system_default_value
71
+ if not override:
72
+ return message.format(value)
73
+ return self._render_overridden_text(message.format(value))
74
+
75
+ def render_job_queues_default(self, obj):
76
+ """
77
+ Render the job's default task queues as JSON.
78
+
79
+ Attempts to retrieve the `task_queues` from the job's class and render
80
+ it using a JSON template. Falls back to a placeholder on error.
81
+ """
82
+ try:
83
+ data = obj.job_class.task_queues
84
+ rendered = render_to_string("extras/inc/json_data.html", {"data": data, "format": "json"})
85
+ except Exception:
86
+ rendered = helpers.placeholder(None)
87
+ return self._render_overridden_block(rendered)
88
+
89
+ def render_default_job_queue_default(self, obj):
90
+ """
91
+ Render the default job queue name from the job class.
92
+
93
+ Retrieves the first queue from `task_queues` if available.
94
+ Falls back to a placeholder if no queues are present.
95
+ """
96
+ try:
97
+ queue = obj.job_class.task_queues[0]
98
+ except (AttributeError, IndexError, TypeError):
99
+ queue = helpers.placeholder("not specified")
100
+ return self._render_overridden_text(queue)
101
+
102
+ def render_boolean_default(self, default_value):
103
+ """Render a boolean default value using a standardized visual format."""
104
+ return self._render_overridden_text(helpers.render_boolean(default_value))
105
+
106
+ def render_override_value(self, key, obj):
107
+ """Render the override value for a given key on a job object."""
108
+ override_attr = f"{key}_override"
109
+
110
+ if not getattr(obj, override_attr, None):
111
+ return ""
112
+
113
+ if not obj.installed:
114
+ return self._render_overridden_text("default is unknown (not currently installed)")
115
+
116
+ try:
117
+ default_value = getattr(obj.job_class, key)
118
+ except Exception:
119
+ default_value = "unknown"
120
+
121
+ renderers = {
122
+ "description": self.render_description_default,
123
+ "soft_time_limit": lambda v: self.render_time_limit_default(
124
+ v, helpers.settings_or_config("CELERY_TASK_SOFT_TIME_LIMIT")
125
+ ),
126
+ "time_limit": lambda v: self.render_time_limit_default(
127
+ v, helpers.settings_or_config("CELERY_TASK_TIME_LIMIT")
128
+ ),
129
+ "job_queues": lambda _: self.render_job_queues_default(obj),
130
+ "default_job_queue": lambda _: self.render_default_job_queue_default(obj),
131
+ }
132
+
133
+ if key in renderers:
134
+ return renderers[key](default_value)
135
+
136
+ if isinstance(default_value, bool):
137
+ return self.render_boolean_default(default_value)
138
+
139
+ return self._render_overridden_text(f'"{default_value}"')
140
+
141
+ def render_job_queues_list(self, value):
142
+ """Renders a <ul> HTML list of job queues with hyperlinks, or a placeholder if none exist."""
143
+ if not value or not value.exists():
144
+ return helpers.placeholder(None)
145
+
146
+ items = format_html_join("\n", "<li>{}</li>", ((helpers.hyperlinked_object(q),) for q in value.all()))
147
+ return format_html("<ul>{}</ul>", items)
148
+
149
+ def render_body_content(self, context: Context):
150
+ """Render the body content of the panel as a table of key-value rows, including any override information."""
151
+ data = self.get_data(context)
152
+ obj = get_obj_from_context(context)
153
+
154
+ if not data:
155
+ return format_html('<tr><td colspan="2">{}</td></tr>', helpers.placeholder(data))
156
+
157
+ result = format_html("")
158
+ panel_label = helpers.slugify(self.label or "")
159
+ for key, value in data.items():
160
+ key_display = self.render_key(key, value, context)
161
+ override_value_display = self.render_override_value(key, obj)
162
+ if value_display := self.render_value(key, value, context):
163
+ if value_display is helpers.HTML_NONE:
164
+ value_tag = value_display
165
+ else:
166
+ value_tag = format_html(
167
+ """
168
+ <span class="hover_copy">
169
+ <span id="{unique_id}_value_{key}">{value}</span>
170
+ <button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{unique_id}_value_{key}">
171
+ <span class="mdi mdi-content-copy"></span>
172
+ </button>
173
+ </span>
174
+ """,
175
+ # key might not be globally unique in a page, but is unique to a panel;
176
+ # Hence we add the panel label to make it globally unique to the page
177
+ unique_id=panel_label,
178
+ key=helpers.slugify(key),
179
+ value=value_display,
180
+ )
181
+ result += format_html(
182
+ "<tr><td>{}</td><td>{}</td><td>{}</td></tr>",
183
+ key_display,
184
+ value_tag,
185
+ override_value_display,
186
+ )
187
+
188
+ return result
189
+
190
+
191
+ class JobObjectFieldsPanel(ObjectFieldsPanel, JobKeyValueOverrideValueTablePanel):
192
+ """
193
+ ObjectFieldsPanel that renders its fields in a 3-column layout.
194
+ Inherits behavior from ObjectFieldsPanel but overrides rendering with JobKeyValueOverrideValueTablePanel.
195
+ """
196
+
197
+ def render_value(self, key, value, context: Context):
198
+ if key == "job_queues":
199
+ return self.render_job_queues_list(value)
200
+ if key == "soft_time_limit":
201
+ value = self.render_time_limit_default(
202
+ value, helpers.settings_or_config("CELERY_TASK_SOFT_TIME_LIMIT"), override=False
203
+ )
204
+ if key == "time_limit":
205
+ value = self.render_time_limit_default(
206
+ value, helpers.settings_or_config("CELERY_TASK_TIME_LIMIT"), override=False
207
+ )
208
+ return super().render_value(key, value, context)
@@ -1,7 +1,6 @@
1
1
  """Models for representing external data sources."""
2
2
 
3
3
  from contextlib import contextmanager
4
- from importlib.util import find_spec
5
4
  import logging
6
5
  import os
7
6
  import shutil
@@ -17,7 +16,8 @@ from nautobot.core.models.fields import AutoSlugField, LaxURLField, slugify_dash
17
16
  from nautobot.core.models.generics import PrimaryModel
18
17
  from nautobot.core.models.validators import EnhancedURLValidator
19
18
  from nautobot.core.utils.git import GitRepo
20
- from nautobot.extras.utils import check_if_key_is_graphql_safe, extras_features
19
+ from nautobot.core.utils.module_loading import check_name_safe_to_import_privately
20
+ from nautobot.extras.utils import extras_features
21
21
 
22
22
  logger = logging.getLogger(__name__)
23
23
 
@@ -105,12 +105,9 @@ class GitRepository(PrimaryModel):
105
105
  )
106
106
 
107
107
  if not self.present_in_database:
108
- check_if_key_is_graphql_safe(self.__class__.__name__, self.slug, "slug")
109
- # Check on create whether the proposed slug conflicts with a module name already in the Python environment.
110
- if find_spec(self.slug) is not None:
111
- raise ValidationError(
112
- f'Please choose a different slug, as "{self.slug}" is an installed Python package or module.'
113
- )
108
+ permitted, reason = check_name_safe_to_import_privately(self.slug)
109
+ if not permitted:
110
+ raise ValidationError({"slug": f"Please choose a different slug; {self.slug!r} is {reason}"})
114
111
 
115
112
  if self.provided_contents:
116
113
  q = models.Q()
@@ -1383,6 +1383,11 @@ class ScheduledJob(BaseModel):
1383
1383
  if job_model.time_limit > 0:
1384
1384
  celery_kwargs["time_limit"] = job_model.time_limit
1385
1385
 
1386
+ # We do this because when a job creates an approval workflow, a scheduled job is also created.
1387
+ # If the scheduled job has an "immediate" interval, the scheduler will not send this task.
1388
+ # since TYPE_IMMEDIATELY is not a valid value in JobExecutionType.SCHEDULE_CHOICES
1389
+ if interval == JobExecutionType.TYPE_IMMEDIATELY:
1390
+ interval = JobExecutionType.TYPE_FUTURE
1386
1391
  # 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
1387
1392
  #
1388
1393
  # We pass in task and job_model here partly for forward/backward compatibility logic, and
@@ -1,5 +1,6 @@
1
1
  from django.contrib.contenttypes.models import ContentType
2
2
  from django.db import models
3
+ from django.utils.html import format_html
3
4
  from taggit.models import GenericUUIDTaggedItemBase
4
5
 
5
6
  from nautobot.core.choices import ColorChoices
@@ -65,6 +66,9 @@ class Tag(
65
66
  class Meta:
66
67
  ordering = ["name"]
67
68
 
69
+ def get_color_display(self):
70
+ return format_html('<span class="label color-block" style="background-color: #{}">&nbsp;</span>', self.color)
71
+
68
72
  def validate_content_types_removal(self, content_types_id):
69
73
  """Validate content_types to be removed are not tagged to a model"""
70
74
  errors = {}
@@ -81,6 +81,9 @@ class NautobotAppConfig(NautobotConfig):
81
81
  config_view_name = None
82
82
  docs_view_name = None
83
83
 
84
+ # Dynamic jobs. Set to True if the app's job code should be reloaded at runtime
85
+ provides_dynamic_jobs = False
86
+
84
87
  # Default integration paths. Plugin authors can override these to customize the paths to
85
88
  # integrated components.
86
89
  banner_function = "banner.banner"
nautobot/extras/tables.py CHANGED
@@ -1,9 +1,13 @@
1
1
  from django.conf import settings
2
+ from django.db.models import QuerySet
2
3
  from django.utils.html import format_html, format_html_join
3
4
  import django_tables2 as tables
5
+ from django_tables2.data import TableData
6
+ from django_tables2.rows import BoundRows
4
7
  from django_tables2.utils import Accessor
5
8
  from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
6
9
 
10
+ from nautobot.core.models.querysets import count_related
7
11
  from nautobot.core.tables import (
8
12
  BaseTable,
9
13
  BooleanColumn,
@@ -19,7 +23,7 @@ from nautobot.core.tables import (
19
23
  from nautobot.core.templatetags.helpers import render_boolean, render_json, render_markdown
20
24
  from nautobot.tenancy.tables import TenantColumn
21
25
 
22
- from .choices import MetadataTypeDataTypeChoices
26
+ from .choices import LogLevelChoices, MetadataTypeDataTypeChoices
23
27
  from .models import (
24
28
  ComputedField,
25
29
  ConfigContext,
@@ -891,7 +895,7 @@ class JobResultTable(BaseTable):
891
895
  )
892
896
  summary = tables.Column(
893
897
  empty_values=(),
894
- verbose_name="Results",
898
+ verbose_name="Summary",
895
899
  orderable=False,
896
900
  attrs={"td": {"class": "text-nowrap report-stats"}},
897
901
  )
@@ -901,6 +905,40 @@ class JobResultTable(BaseTable):
901
905
  )
902
906
  actions = ButtonsColumn(JobResult, buttons=("delete",), prepend_template=JOB_RESULT_BUTTONS)
903
907
 
908
+ def __init__(self, *args, **kwargs):
909
+ super().__init__(*args, **kwargs)
910
+ # Only calculate log counts for "summary" column if it's actually visible.
911
+ if "summary" in self.columns and self.columns["summary"].visible and isinstance(self.data.data, QuerySet):
912
+ self.data = TableData.from_data(
913
+ self.data.data.annotate(
914
+ debug_log_count=count_related(
915
+ JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_DEBUG}
916
+ ),
917
+ success_log_count=count_related(
918
+ JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_SUCCESS}
919
+ ),
920
+ info_log_count=count_related(
921
+ JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_INFO}
922
+ ),
923
+ warning_log_count=count_related(
924
+ JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_WARNING}
925
+ ),
926
+ error_log_count=count_related(
927
+ JobLogEntry,
928
+ "job_result",
929
+ filter_dict={
930
+ "log_level__in": [
931
+ LogLevelChoices.LOG_FAILURE,
932
+ LogLevelChoices.LOG_ERROR,
933
+ LogLevelChoices.LOG_CRITICAL,
934
+ ],
935
+ },
936
+ ),
937
+ )
938
+ )
939
+ self.data.set_table(self)
940
+ self.rows = BoundRows(data=self.data, table=self, pinned_data=self.pinned_data)
941
+
904
942
  def render_summary(self, record):
905
943
  """
906
944
  Define custom rendering for the summary column.
@@ -940,7 +978,6 @@ class JobResultTable(BaseTable):
940
978
  "job_model",
941
979
  "user",
942
980
  "status",
943
- "summary",
944
981
  "actions",
945
982
  )
946
983