nautobot 2.0.5__py3-none-any.whl → 2.1.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 (376) hide show
  1. nautobot/circuits/navigation.py +0 -25
  2. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -9
  3. nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
  4. nautobot/circuits/tests/test_filters.py +1 -0
  5. nautobot/core/api/serializers.py +15 -5
  6. nautobot/core/api/views.py +18 -19
  7. nautobot/core/choices.py +1 -1
  8. nautobot/core/filters.py +12 -4
  9. nautobot/core/jobs/__init__.py +125 -3
  10. nautobot/core/management/commands/generate_test_data.py +4 -1
  11. nautobot/core/models/fields.py +12 -2
  12. nautobot/core/settings.py +8 -7
  13. nautobot/core/templates/base_django.html +2 -2
  14. nautobot/core/templates/buttons/export.html +57 -30
  15. nautobot/core/templates/generic/object_list.html +2 -2
  16. nautobot/core/templates/generic/object_retrieve.html +8 -1
  17. nautobot/core/templates/home.html +5 -5
  18. nautobot/core/templates/inc/created_updated.html +2 -2
  19. nautobot/core/templates/inc/footer.html +2 -2
  20. nautobot/core/templates/inc/javascript.html +0 -10
  21. nautobot/core/templates/inc/media.html +2 -0
  22. nautobot/core/templates/inc/nav_menu.html +66 -68
  23. nautobot/core/templates/inc/object_details_advanced_panel.html +19 -0
  24. nautobot/core/templates/nautobot_config.py.j2 +10 -4
  25. nautobot/core/templates/panel_table.html +1 -1
  26. nautobot/core/templates/template.css +89 -0
  27. nautobot/core/templates/utilities/templatetags/table_config_form.html +1 -0
  28. nautobot/core/templatetags/buttons.py +7 -2
  29. nautobot/core/testing/views.py +34 -4
  30. nautobot/core/tests/integration/test_home.py +1 -43
  31. nautobot/core/tests/integration/test_navbar.py +10 -64
  32. nautobot/core/tests/integration/test_plugin_home.py +4 -5
  33. nautobot/core/tests/integration/test_plugin_navbar.py +20 -16
  34. nautobot/core/tests/integration/test_theme.py +4 -0
  35. nautobot/core/tests/test_api.py +14 -66
  36. nautobot/core/tests/test_filters.py +127 -0
  37. nautobot/core/tests/test_forms.py +1 -1
  38. nautobot/core/tests/test_graphql.py +165 -2
  39. nautobot/core/tests/test_jobs.py +112 -0
  40. nautobot/core/tests/test_openapi.py +6 -0
  41. nautobot/core/tests/test_views.py +11 -85
  42. nautobot/core/urls.py +6 -1
  43. nautobot/core/utils/lookup.py +28 -0
  44. nautobot/core/utils/requests.py +2 -3
  45. nautobot/core/views/__init__.py +3 -4
  46. nautobot/core/views/generic.py +9 -4
  47. nautobot/core/views/mixins.py +4 -2
  48. nautobot/core/views/renderers.py +5 -0
  49. nautobot/dcim/models/device_components.py +1 -0
  50. nautobot/dcim/navigation.py +10 -165
  51. nautobot/dcim/templates/dcim/location.html +1 -1
  52. nautobot/dcim/tests/features/locations.feature +143 -0
  53. nautobot/dcim/tests/test_api.py +1 -1
  54. nautobot/dcim/tests/test_filters.py +11 -3
  55. nautobot/extras/admin.py +1 -1
  56. nautobot/extras/api/serializers.py +33 -0
  57. nautobot/extras/api/urls.py +6 -0
  58. nautobot/extras/api/views.py +45 -6
  59. nautobot/extras/factory.py +28 -2
  60. nautobot/extras/filters/__init__.py +52 -0
  61. nautobot/extras/filters/mixins.py +4 -29
  62. nautobot/extras/forms/forms.py +43 -0
  63. nautobot/extras/jobs.py +31 -9
  64. nautobot/extras/migrations/0100_fileproxy_job_result.py +32 -0
  65. nautobot/extras/migrations/0101_externalintegration.py +61 -0
  66. nautobot/extras/migrations/0102_set_null_objectchange_contenttype.py +32 -0
  67. nautobot/extras/models/__init__.py +2 -0
  68. nautobot/extras/models/change_logging.py +2 -2
  69. nautobot/extras/models/models.py +96 -16
  70. nautobot/extras/navigation.py +17 -29
  71. nautobot/extras/signals.py +15 -0
  72. nautobot/extras/tables.py +27 -0
  73. nautobot/extras/templates/extras/externalintegration_retrieve.html +37 -0
  74. nautobot/extras/templates/extras/inc/jobresult.html +24 -0
  75. nautobot/extras/templates/extras/jobresult.html +24 -0
  76. nautobot/extras/test_jobs/file_output.py +16 -0
  77. nautobot/extras/tests/test_api.py +92 -0
  78. nautobot/extras/tests/test_filters.py +64 -2
  79. nautobot/extras/tests/test_jobs.py +39 -0
  80. nautobot/extras/tests/test_models.py +34 -0
  81. nautobot/extras/tests/test_views.py +22 -2
  82. nautobot/extras/urls.py +1 -0
  83. nautobot/extras/views.py +15 -0
  84. nautobot/ipam/forms.py +16 -0
  85. nautobot/ipam/models.py +3 -0
  86. nautobot/ipam/navigation.py +2 -59
  87. nautobot/ipam/templates/ipam/ipaddress.html +0 -9
  88. nautobot/ipam/templates/ipam/prefix.html +0 -9
  89. nautobot/ipam/tests/features/prefixes.feature +134 -0
  90. nautobot/ipam/tests/test_filters.py +5 -10
  91. nautobot/ipam/tests/test_views.py +8 -1
  92. nautobot/ipam/views.py +3 -0
  93. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +191 -191
  94. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  95. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +1 -1
  96. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  97. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +874 -881
  98. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  99. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  100. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  101. nautobot/project-static/css/base.css +135 -99
  102. nautobot/project-static/css/dark.css +65 -6
  103. nautobot/project-static/docs/404.html +44 -16
  104. nautobot/project-static/docs/apps/index.html +44 -16
  105. nautobot/project-static/docs/apps/nautobot-apps.html +44 -16
  106. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +44 -16
  107. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +44 -16
  108. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1597 -1457
  109. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +44 -16
  110. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +45 -17
  111. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +44 -16
  112. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +44 -16
  113. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +44 -16
  114. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +44 -16
  115. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +353 -432
  116. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +66 -38
  117. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +44 -16
  118. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2154 -2307
  119. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +807 -691
  120. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +44 -16
  121. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +44 -16
  122. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +58 -30
  123. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +3600 -3456
  124. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +44 -16
  125. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +44 -16
  126. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +101 -75
  127. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +3083 -3019
  128. nautobot/project-static/docs/development/apps/api/configuration-view.html +44 -16
  129. nautobot/project-static/docs/development/apps/api/database-backend-config.html +44 -16
  130. nautobot/project-static/docs/development/apps/api/models/django-admin.html +44 -16
  131. nautobot/project-static/docs/development/apps/api/models/global-search.html +44 -16
  132. nautobot/project-static/docs/development/apps/api/models/graphql.html +44 -16
  133. nautobot/project-static/docs/development/apps/api/models/index.html +44 -16
  134. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +44 -16
  135. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +44 -16
  136. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +44 -16
  137. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +44 -16
  138. nautobot/project-static/docs/development/apps/api/platform-features/index.html +44 -16
  139. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +44 -16
  140. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +44 -16
  141. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +44 -16
  142. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +44 -16
  143. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +44 -16
  144. nautobot/project-static/docs/development/apps/api/prometheus.html +44 -16
  145. nautobot/project-static/docs/development/apps/api/setup.html +44 -16
  146. nautobot/project-static/docs/development/apps/api/testing.html +44 -16
  147. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +44 -16
  148. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +44 -16
  149. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +44 -16
  150. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +44 -16
  151. nautobot/project-static/docs/development/apps/api/ui-extensions/object-detail-views.html +44 -16
  152. nautobot/project-static/docs/development/apps/api/ui-extensions/tabs.html +44 -16
  153. nautobot/project-static/docs/development/apps/api/views/base-template.html +44 -16
  154. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +44 -16
  155. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +44 -16
  156. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +44 -16
  157. nautobot/project-static/docs/development/apps/api/views/index.html +44 -16
  158. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +44 -16
  159. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +44 -16
  160. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +44 -16
  161. nautobot/project-static/docs/development/apps/api/views/notes.html +44 -16
  162. nautobot/project-static/docs/development/apps/api/views/rest-api.html +44 -16
  163. nautobot/project-static/docs/development/apps/api/views/urls.html +44 -16
  164. nautobot/project-static/docs/development/apps/api/views/view-overrides.html +44 -16
  165. nautobot/project-static/docs/development/apps/index.html +44 -16
  166. nautobot/project-static/docs/development/apps/migration/code-updates.html +44 -16
  167. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +44 -16
  168. nautobot/project-static/docs/development/apps/migration/from-v1.html +44 -16
  169. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +44 -16
  170. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +44 -16
  171. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +44 -16
  172. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +44 -16
  173. nautobot/project-static/docs/development/apps/porting-from-netbox.html +44 -16
  174. nautobot/project-static/docs/development/core/application-registry.html +44 -16
  175. nautobot/project-static/docs/development/core/best-practices.html +44 -16
  176. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +44 -16
  177. nautobot/project-static/docs/development/core/extending-models.html +44 -16
  178. nautobot/project-static/docs/development/core/generic-views.html +44 -16
  179. nautobot/project-static/docs/development/core/getting-started.html +44 -16
  180. nautobot/project-static/docs/development/core/homepage.html +44 -16
  181. nautobot/project-static/docs/development/core/index.html +44 -16
  182. nautobot/project-static/docs/development/core/model-features.html +44 -16
  183. nautobot/project-static/docs/development/core/natural-keys.html +44 -16
  184. nautobot/project-static/docs/development/core/navigation-menu.html +44 -21
  185. nautobot/project-static/docs/development/core/react-ui.html +44 -16
  186. nautobot/project-static/docs/development/core/release-checklist.html +44 -16
  187. nautobot/project-static/docs/development/core/role-internals.html +44 -16
  188. nautobot/project-static/docs/development/core/style-guide.html +44 -16
  189. nautobot/project-static/docs/development/core/templates.html +44 -16
  190. nautobot/project-static/docs/development/core/testing.html +44 -16
  191. nautobot/project-static/docs/development/core/user-preferences.html +44 -16
  192. nautobot/project-static/docs/development/index.html +44 -16
  193. nautobot/project-static/docs/development/jobs/index.html +280 -234
  194. nautobot/project-static/docs/development/jobs/migration/from-v1.html +44 -16
  195. nautobot/project-static/docs/index.html +44 -16
  196. nautobot/project-static/docs/objects.inv +0 -0
  197. nautobot/project-static/docs/release-notes/index.html +47 -19
  198. nautobot/project-static/docs/release-notes/version-1.0.html +44 -16
  199. nautobot/project-static/docs/release-notes/version-1.1.html +44 -16
  200. nautobot/project-static/docs/release-notes/version-1.2.html +44 -16
  201. nautobot/project-static/docs/release-notes/version-1.3.html +44 -16
  202. nautobot/project-static/docs/release-notes/version-1.4.html +44 -16
  203. nautobot/project-static/docs/release-notes/version-1.5.html +44 -16
  204. nautobot/project-static/docs/release-notes/version-1.6.html +44 -16
  205. nautobot/project-static/docs/release-notes/version-2.0.html +47 -19
  206. nautobot/project-static/docs/release-notes/version-2.1.html +5724 -0
  207. nautobot/project-static/docs/search/search_index.json +1 -1
  208. nautobot/project-static/docs/sitemap.xml +247 -237
  209. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  210. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +44 -16
  211. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +44 -16
  212. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +44 -16
  213. nautobot/project-static/docs/user-guide/administration/configuration/index.html +44 -16
  214. nautobot/project-static/docs/user-guide/administration/configuration/node-configuration.html +44 -16
  215. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +109 -43
  216. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +44 -16
  217. nautobot/project-static/docs/user-guide/administration/guides/caching.html +44 -16
  218. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +44 -16
  219. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +44 -16
  220. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +44 -16
  221. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +44 -16
  222. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +44 -16
  223. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +48 -19
  224. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +44 -16
  225. nautobot/project-static/docs/user-guide/administration/installation/docker.html +44 -16
  226. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +44 -16
  227. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +44 -16
  228. nautobot/project-static/docs/user-guide/administration/installation/index.html +44 -16
  229. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +44 -16
  230. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +44 -16
  231. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +44 -16
  232. nautobot/project-static/docs/user-guide/administration/installation/services.html +44 -16
  233. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +44 -16
  234. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +44 -16
  235. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +44 -16
  236. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +44 -16
  237. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +44 -16
  238. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +44 -16
  239. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +44 -16
  240. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +44 -16
  241. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +44 -16
  242. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +44 -16
  243. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +44 -16
  244. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +44 -16
  245. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +44 -16
  246. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +44 -16
  247. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +44 -16
  248. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +44 -16
  249. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +44 -16
  250. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +44 -16
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +44 -16
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +44 -16
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +44 -16
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +44 -16
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +44 -16
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +44 -16
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +44 -16
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +44 -16
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +44 -16
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +44 -16
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +44 -16
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +44 -16
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +44 -16
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +44 -16
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +44 -16
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +44 -16
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +44 -16
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +44 -16
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +44 -16
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +44 -16
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +44 -16
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +44 -16
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +44 -16
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +44 -16
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +44 -16
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +44 -16
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +44 -16
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +44 -16
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +44 -16
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +44 -16
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +44 -16
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +44 -16
  283. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +44 -16
  284. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +44 -16
  285. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +44 -16
  286. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +44 -16
  287. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +44 -16
  288. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +44 -16
  289. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +44 -16
  290. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +44 -16
  291. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +44 -16
  292. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +44 -16
  293. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +44 -16
  294. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +44 -16
  295. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +44 -16
  296. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +44 -16
  297. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +44 -16
  298. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +44 -16
  299. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +44 -16
  300. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +44 -16
  301. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +44 -16
  302. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +44 -16
  303. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +44 -16
  304. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +44 -16
  305. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +44 -16
  306. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +44 -16
  307. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +44 -16
  308. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +44 -16
  309. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +44 -16
  310. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +44 -16
  311. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +44 -16
  312. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +44 -16
  313. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +44 -16
  314. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +44 -16
  315. nautobot/project-static/docs/user-guide/index.html +44 -16
  316. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +110 -16
  317. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +44 -16
  318. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +44 -16
  319. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +44 -16
  320. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +44 -16
  321. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +47 -19
  322. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +5359 -0
  323. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +47 -19
  324. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +44 -16
  325. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +44 -16
  326. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +44 -16
  327. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +44 -16
  328. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +44 -16
  329. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +44 -16
  330. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +44 -16
  331. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +44 -16
  332. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +44 -16
  333. nautobot/project-static/docs/user-guide/platform-functionality/note.html +44 -16
  334. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +44 -16
  335. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +44 -16
  336. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +113 -44
  337. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +44 -16
  338. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +44 -16
  339. nautobot/project-static/docs/user-guide/platform-functionality/role.html +44 -16
  340. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +44 -16
  341. nautobot/project-static/docs/user-guide/platform-functionality/status.html +44 -16
  342. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +44 -16
  343. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +44 -16
  344. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +44 -16
  345. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +44 -16
  346. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +44 -16
  347. nautobot/project-static/fonts/UFL.txt +96 -0
  348. nautobot/project-static/fonts/Ubuntu-Bold.woff2 +0 -0
  349. nautobot/project-static/fonts/Ubuntu-BoldItalic.woff2 +0 -0
  350. nautobot/project-static/fonts/Ubuntu-Italic.woff2 +0 -0
  351. nautobot/project-static/fonts/Ubuntu-Medium.woff2 +0 -0
  352. nautobot/project-static/fonts/Ubuntu-MediumItalic.woff2 +0 -0
  353. nautobot/project-static/fonts/Ubuntu-Regular.woff2 +0 -0
  354. nautobot/project-static/fonts/UbuntuMono-Bold.woff2 +0 -0
  355. nautobot/project-static/fonts/UbuntuMono-BoldItalic.woff2 +0 -0
  356. nautobot/project-static/fonts/UbuntuMono-Italic.woff2 +0 -0
  357. nautobot/project-static/fonts/UbuntuMono-Regular.woff2 +0 -0
  358. nautobot/project-static/img/dark-theme.png +0 -0
  359. nautobot/project-static/img/light-theme.png +0 -0
  360. nautobot/project-static/img/nautobot_chevron.svg +5 -0
  361. nautobot/project-static/img/nautobot_chevron_header.svg +5 -0
  362. nautobot/project-static/img/system-theme.png +0 -0
  363. nautobot/tenancy/navigation.py +0 -13
  364. nautobot/ui/package-lock.json +2 -2
  365. nautobot/ui/package.json +1 -1
  366. nautobot/users/admin.py +44 -0
  367. nautobot/users/migrations/0007_alter_objectpermission_object_types.py +33 -0
  368. nautobot/users/models.py +3 -2
  369. nautobot/virtualization/navigation.py +1 -33
  370. nautobot/virtualization/tests/test_api.py +1 -1
  371. nautobot/virtualization/tests/test_filters.py +1 -1
  372. {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/METADATA +1 -1
  373. {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/RECORD +376 -351
  374. {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/LICENSE.txt +0 -0
  375. {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/WHEEL +0 -0
  376. {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/entry_points.txt +0 -0
@@ -51,6 +51,8 @@ from nautobot.extras.models import (
51
51
  DynamicGroup,
52
52
  DynamicGroupMembership,
53
53
  ExportTemplate,
54
+ ExternalIntegration,
55
+ FileProxy,
54
56
  GitRepository,
55
57
  GraphQLQuery,
56
58
  ImageAttachment,
@@ -293,6 +295,28 @@ class ExportTemplateSerializer(RelationshipModelSerializerMixin, ValidatedModelS
293
295
  return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.owner, "owner")
294
296
 
295
297
 
298
+ #
299
+ # External integrations
300
+ #
301
+
302
+
303
+ class ExternalIntegrationSerializer(NautobotModelSerializer):
304
+ class Meta:
305
+ model = ExternalIntegration
306
+ fields = "__all__"
307
+
308
+
309
+ #
310
+ # File proxies
311
+ #
312
+
313
+
314
+ class FileProxySerializer(BaseModelSerializer):
315
+ class Meta:
316
+ model = FileProxy
317
+ exclude = ["file"]
318
+
319
+
296
320
  #
297
321
  # Git repositories
298
322
  #
@@ -444,6 +468,15 @@ class JobResultSerializer(CustomFieldModelSerializerMixin, BaseModelSerializer):
444
468
  class Meta:
445
469
  model = JobResult
446
470
  fields = "__all__"
471
+ extra_kwargs = {
472
+ "files": {"read_only": True},
473
+ }
474
+
475
+ def get_field_names(self, declared_fields, info):
476
+ """Add reverse relation to related FileProxy objects."""
477
+ fields = list(super().get_field_names(declared_fields, info))
478
+ self.extend_field_names(fields, "files")
479
+ return fields
447
480
 
448
481
 
449
482
  class JobRunResponseSerializer(serializers.Serializer):
@@ -31,6 +31,12 @@ router.register("dynamic-group-memberships", views.DynamicGroupMembershipViewSet
31
31
  # Export templates
32
32
  router.register("export-templates", views.ExportTemplateViewSet)
33
33
 
34
+ # External integrations
35
+ router.register("external-integrations", views.ExternalIntegrationViewSet)
36
+
37
+ # File proxies
38
+ router.register("file-proxies", views.FileProxyViewSet)
39
+
34
40
  # Git repositories
35
41
  router.register("git-repositories", views.GitRepositoryViewSet)
36
42
 
@@ -1,22 +1,22 @@
1
1
  from datetime import timedelta
2
+
2
3
  from django.conf import settings
3
4
  from django.contrib.contenttypes.models import ContentType
4
5
  from django.forms import ValidationError as FormsValidationError
5
- from django.http import Http404
6
+ from django.http import FileResponse, Http404
6
7
  from django.shortcuts import get_object_or_404
7
8
  from django.utils import timezone
8
9
  from drf_spectacular.types import OpenApiTypes
9
- from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter
10
+ from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
10
11
  from graphene_django.views import GraphQLView
11
12
  from graphql import GraphQLError
12
- from rest_framework import status
13
+ from rest_framework import mixins, status, viewsets
13
14
  from rest_framework.decorators import action
14
15
  from rest_framework.exceptions import MethodNotAllowed, PermissionDenied, ValidationError
15
16
  from rest_framework.parsers import JSONParser, MultiPartParser
16
17
  from rest_framework.permissions import IsAuthenticated
17
18
  from rest_framework.response import Response
18
19
  from rest_framework.routers import APIRootView
19
- from rest_framework import mixins, viewsets
20
20
 
21
21
  from nautobot.core.api.authentication import TokenPermissions
22
22
  from nautobot.core.api.utils import get_serializer_for_model
@@ -38,10 +38,14 @@ from nautobot.extras.models import (
38
38
  ComputedField,
39
39
  ConfigContext,
40
40
  ConfigContextSchema,
41
+ CustomField,
42
+ CustomFieldChoice,
43
+ CustomLink,
41
44
  DynamicGroup,
42
45
  DynamicGroupMembership,
43
- CustomLink,
44
46
  ExportTemplate,
47
+ ExternalIntegration,
48
+ FileProxy,
45
49
  GitRepository,
46
50
  GraphQLQuery,
47
51
  ImageAttachment,
@@ -64,9 +68,9 @@ from nautobot.extras.models import (
64
68
  TaggedItem,
65
69
  Webhook,
66
70
  )
67
- from nautobot.extras.models import CustomField, CustomFieldChoice
68
71
  from nautobot.extras.secrets.exceptions import SecretError
69
72
  from nautobot.extras.utils import get_worker_count
73
+
70
74
  from . import serializers
71
75
 
72
76
 
@@ -321,6 +325,41 @@ class ExportTemplateViewSet(NotesViewSetMixin, ModelViewSet):
321
325
  filterset_class = filters.ExportTemplateFilterSet
322
326
 
323
327
 
328
+ #
329
+ # External integrations
330
+ #
331
+
332
+
333
+ class ExternalIntegrationViewSet(NautobotModelViewSet):
334
+ queryset = ExternalIntegration.objects.select_related("secrets_group")
335
+ serializer_class = serializers.ExternalIntegrationSerializer
336
+ filterset_class = filters.ExternalIntegrationFilterSet
337
+
338
+
339
+ #
340
+ # File proxies
341
+ #
342
+
343
+
344
+ class FileProxyViewSet(ReadOnlyModelViewSet):
345
+ queryset = FileProxy.objects.select_related("job_result")
346
+ serializer_class = serializers.FileProxySerializer
347
+ filterset_class = filters.FileProxyFilterSet
348
+
349
+ @extend_schema(
350
+ methods=["get"],
351
+ responses=OpenApiTypes.BINARY,
352
+ )
353
+ @action(
354
+ detail=True,
355
+ methods=["get"],
356
+ )
357
+ def download(self, request, *args, **kwargs):
358
+ """Download the specified FileProxy."""
359
+ file_proxy = self.get_object()
360
+ return FileResponse(file_proxy.file.open("rb"), as_attachment=True)
361
+
362
+
324
363
  #
325
364
  # Git repositories
326
365
  #
@@ -3,11 +3,37 @@ import factory
3
3
  import faker
4
4
 
5
5
  from nautobot.core.choices import ColorChoices
6
- from nautobot.core.factory import NautobotBoolIterator, OrganizationalModelFactory, get_random_instances
7
- from nautobot.extras.models import Role, Status, Tag
6
+ from nautobot.core.factory import (
7
+ get_random_instances,
8
+ NautobotBoolIterator,
9
+ OrganizationalModelFactory,
10
+ PrimaryModelFactory,
11
+ UniqueFaker,
12
+ )
13
+ from nautobot.extras.models import ExternalIntegration, Role, Status, Tag
8
14
  from nautobot.extras.utils import FeatureQuery, RoleModelsQuery, TaggableClassesQuery
9
15
 
10
16
 
17
+ class ExternalIntegrationFactory(PrimaryModelFactory):
18
+ """ExternalIntegration model factory."""
19
+
20
+ class Meta:
21
+ model = ExternalIntegration
22
+
23
+ class Params:
24
+ has_extra_config = NautobotBoolIterator()
25
+
26
+ name = UniqueFaker("bs")
27
+ remote_url = factory.Faker("url", schemes=["http", "https", "ssh"])
28
+ verify_ssl = factory.Faker("boolean")
29
+ timeout = factory.Faker("pyint", min_value=0, max_value=300)
30
+ extra_config = factory.Maybe(
31
+ "has_extra_config",
32
+ factory.Faker("pydict", allowed_types=[bool, int, str]),
33
+ None,
34
+ )
35
+
36
+
11
37
  class RoleFactory(OrganizationalModelFactory):
12
38
  """Role model factory."""
13
39
 
@@ -9,6 +9,7 @@ from nautobot.core.filters import (
9
9
  ContentTypeMultipleChoiceFilter,
10
10
  MultiValueUUIDFilter,
11
11
  NaturalKeyOrPKMultipleChoiceFilter,
12
+ RelatedMembershipBooleanFilter,
12
13
  SearchFilter,
13
14
  )
14
15
  from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
@@ -52,6 +53,8 @@ from nautobot.extras.models import (
52
53
  DynamicGroup,
53
54
  DynamicGroupMembership,
54
55
  ExportTemplate,
56
+ ExternalIntegration,
57
+ FileProxy,
55
58
  GitRepository,
56
59
  GraphQLQuery,
57
60
  ImageAttachment,
@@ -101,6 +104,7 @@ __all__ = (
101
104
  "DynamicGroupFilterSet",
102
105
  "DynamicGroupMembershipFilterSet",
103
106
  "ExportTemplateFilterSet",
107
+ "FileProxyFilterSet",
104
108
  "GitRepositoryFilterSet",
105
109
  "GraphQLQueryFilterSet",
106
110
  "ImageAttachmentFilterSet",
@@ -484,6 +488,54 @@ class ExportTemplateFilterSet(BaseFilterSet):
484
488
  fields = ["id", "content_type", "owner_content_type", "owner_object_id", "name"]
485
489
 
486
490
 
491
+ #
492
+ # External integrations
493
+ #
494
+
495
+
496
+ class ExternalIntegrationFilterSet(NautobotFilterSet):
497
+ has_secrets_group = RelatedMembershipBooleanFilter(
498
+ field_name="secrets_group",
499
+ label="Has secrets group",
500
+ )
501
+ secrets_group = NaturalKeyOrPKMultipleChoiceFilter(
502
+ queryset=SecretsGroup.objects.all(),
503
+ label="Secrets group (ID or name)",
504
+ )
505
+
506
+ class Meta:
507
+ model = ExternalIntegration
508
+ fields = "__all__"
509
+
510
+
511
+ #
512
+ # File proxies
513
+ #
514
+
515
+
516
+ class FileProxyFilterSet(BaseFilterSet):
517
+ q = SearchFilter(
518
+ filter_predicates={
519
+ "name": "icontains",
520
+ "job_result__job_model__name": "icontains",
521
+ },
522
+ )
523
+ job = NaturalKeyOrPKMultipleChoiceFilter(
524
+ field_name="job_result__job_model",
525
+ to_field_name="name",
526
+ queryset=Job.objects.all(),
527
+ label="Job (name or ID)",
528
+ )
529
+ job_result_id = django_filters.ModelMultipleChoiceFilter(
530
+ queryset=JobResult.objects.all(),
531
+ label="Job Result (ID)",
532
+ )
533
+
534
+ class Meta:
535
+ model = FileProxy
536
+ fields = ["id", "name", "uploaded_at", "job", "job_result_id"]
537
+
538
+
487
539
  #
488
540
  # Datasources (Git)
489
541
  #
@@ -291,19 +291,13 @@ class RelationshipModelFilterSetMixin(django_filters.FilterSet):
291
291
 
292
292
 
293
293
  class RoleFilter(NaturalKeyOrPKMultipleChoiceFilter):
294
- """Limit role choices to the available role choices for self.model"""
294
+ """Filter field used for filtering Role fields."""
295
295
 
296
296
  def __init__(self, *args, **kwargs):
297
- kwargs.setdefault("field_name", "role")
298
297
  kwargs.setdefault("queryset", Role.objects.all())
299
- kwargs.setdefault("to_field_name", "name")
300
298
  kwargs.setdefault("label", "Role (name or ID)")
301
-
302
299
  super().__init__(*args, **kwargs)
303
300
 
304
- def get_queryset(self, request):
305
- return self.queryset.get_for_model(self.model)
306
-
307
301
 
308
302
  class RoleModelFilterSetMixin(django_filters.FilterSet):
309
303
  """
@@ -313,35 +307,16 @@ class RoleModelFilterSetMixin(django_filters.FilterSet):
313
307
  role = RoleFilter()
314
308
 
315
309
 
316
- class StatusFilter(django_filters.ModelMultipleChoiceFilter):
310
+ class StatusFilter(NaturalKeyOrPKMultipleChoiceFilter):
317
311
  """
318
312
  Filter field used for filtering Status fields.
319
-
320
- Explicitly sets `to_field_name='value'` and dynamically sets queryset to
321
- retrieve choices for the corresponding model & field name bound to the
322
- filterset.
323
313
  """
324
314
 
325
315
  def __init__(self, *args, **kwargs):
326
- kwargs["to_field_name"] = "name"
316
+ kwargs.setdefault("queryset", Status.objects.all())
317
+ kwargs.setdefault("label", "Status (name or ID)")
327
318
  super().__init__(*args, **kwargs)
328
319
 
329
- def get_queryset(self, request):
330
- self.queryset = Status.objects.all()
331
- return super().get_queryset(request)
332
-
333
- def get_filter_predicate(self, value):
334
- """Always use the field's name and the `to_field_name` attribute as predicate."""
335
- # e.g. `status__name`
336
- to_field_name = self.field.to_field_name
337
- name = f"{self.field_name}__{to_field_name}"
338
- # Sometimes the incoming value is an instance. This block of logic comes from the base
339
- # `get_filter_predicate()` and was added here to support this.
340
- try:
341
- return {name: getattr(value, to_field_name)}
342
- except (AttributeError, TypeError):
343
- return {name: value}
344
-
345
320
 
346
321
  class StatusModelFilterSetMixin(django_filters.FilterSet):
347
322
  """
@@ -1,3 +1,5 @@
1
+ import inspect
2
+
1
3
  from django import forms
2
4
  from django.conf import settings
3
5
  from django.contrib.auth import get_user_model
@@ -49,6 +51,7 @@ from nautobot.extras.models import (
49
51
  DynamicGroup,
50
52
  DynamicGroupMembership,
51
53
  ExportTemplate,
54
+ ExternalIntegration,
52
55
  GitRepository,
53
56
  GraphQLQuery,
54
57
  ImageAttachment,
@@ -108,6 +111,8 @@ __all__ = (
108
111
  "DynamicGroupMembershipFormSet",
109
112
  "ExportTemplateForm",
110
113
  "ExportTemplateFilterForm",
114
+ "ExternalIntegrationForm",
115
+ "ExternalIntegrationBulkEditForm",
111
116
  "GitRepositoryForm",
112
117
  "GitRepositoryBulkEditForm",
113
118
  "GitRepositoryFilterForm",
@@ -568,6 +573,44 @@ class ExportTemplateFilterForm(BootstrapMixin, forms.Form):
568
573
  )
569
574
 
570
575
 
576
+ #
577
+ # External integrations
578
+ #
579
+
580
+
581
+ class ExternalIntegrationForm(NautobotModelForm):
582
+ class Meta:
583
+ model = ExternalIntegration
584
+ fields = "__all__"
585
+
586
+ EXTRA_CONFIG_HELP_TEXT = """
587
+ Optional user-defined <a href="https://json.org/">JSON</a> data for this integration. Example:
588
+ <pre><code>{
589
+ "headers": {
590
+ "Accept": "application/json",
591
+ "Content-Type": "application/json",
592
+ },
593
+ }</code></pre>
594
+ """
595
+ help_texts = {"extra_config": inspect.cleandoc(EXTRA_CONFIG_HELP_TEXT)}
596
+
597
+
598
+ class ExternalIntegrationBulkEditForm(NautobotBulkEditForm):
599
+ pk = forms.ModelMultipleChoiceField(
600
+ queryset=ExternalIntegration.objects.all(),
601
+ widget=forms.MultipleHiddenInput(),
602
+ )
603
+ remote_url = forms.CharField(required=False, label="Remote URL")
604
+ secrets_group = DynamicModelChoiceField(required=False, queryset=SecretsGroup.objects.all())
605
+ verify_ssl = forms.NullBooleanField(required=False, label="Verify SSL", widget=BulkEditNullBooleanSelect)
606
+ timeout = forms.IntegerField(required=False, min_value=0)
607
+ extra_config = forms.JSONField(required=False)
608
+
609
+ class Meta:
610
+ model = ExternalIntegration
611
+ nullable_fields = ["extra_config", "secrets_group"]
612
+
613
+
571
614
  #
572
615
  # Git repositories and other data sources
573
616
  #
nautobot/extras/jobs.py CHANGED
@@ -21,6 +21,7 @@ from db_file_storage.form_widgets import DBClearableFileInput
21
21
  from django import forms
22
22
  from django.conf import settings
23
23
  from django.contrib.auth import get_user_model
24
+ from django.core.files.base import ContentFile
24
25
  from django.core.files.uploadedfile import InMemoryUploadedFile
25
26
  from django.core.validators import RegexValidator
26
27
  from django.db.models import Model
@@ -306,7 +307,7 @@ class BaseJob(Task):
306
307
  file_fields = list(self._get_file_vars())
307
308
  file_ids = [kwargs[f] for f in file_fields]
308
309
  if file_ids:
309
- self.delete_files(*file_ids)
310
+ self._delete_file_proxies(*file_ids)
310
311
 
311
312
  self.logger.info("Job completed", extra={"grouping": "post_run"})
312
313
 
@@ -531,8 +532,10 @@ class BaseJob(Task):
531
532
  @classmethod
532
533
  def _get_vars(cls):
533
534
  """
534
- Return dictionary of ScriptVariable attributes defined on this class and any base classes to the top of the inheritance chain.
535
- The variables are sorted in the order that they were defined, with variables defined on base classes appearing before subclass variables.
535
+ Return dictionary of ScriptVariable attributes defined on this class or any of its base parent classes.
536
+
537
+ The variables are sorted in the order that they were defined,
538
+ with variables defined on base classes appearing before subclass variables.
536
539
  """
537
540
  cls_vars = {}
538
541
  # get list of base classes, including cls, in reverse method resolution order: [BaseJob, Job, cls]
@@ -661,7 +664,7 @@ class BaseJob(Task):
661
664
  return_data[field_name] = value.pk
662
665
  # FileVar (Save each FileVar as a FileProxy)
663
666
  elif isinstance(value, InMemoryUploadedFile):
664
- return_data[field_name] = BaseJob.save_file(value)
667
+ return_data[field_name] = BaseJob._save_file_to_proxy(value)
665
668
  # IPAddressVar, IPAddressWithMaskVar, IPNetworkVar
666
669
  elif isinstance(value, netaddr.ip.BaseIP):
667
670
  return_data[field_name] = str(value)
@@ -721,7 +724,7 @@ class BaseJob(Task):
721
724
  else:
722
725
  return_data[field_name] = var.field_attrs["queryset"].get(pk=value)
723
726
  elif isinstance(var, FileVar):
724
- return_data[field_name] = cls.load_file(value)
727
+ return_data[field_name] = cls._load_file_from_proxy(value)
725
728
  # IPAddressVar is a netaddr.IPAddress object
726
729
  elif isinstance(var, IPAddressVar):
727
730
  return_data[field_name] = netaddr.IPAddress(value)
@@ -758,20 +761,20 @@ class BaseJob(Task):
758
761
  return {k: v for k, v in job_kwargs.items() if k in job_vars}
759
762
 
760
763
  @staticmethod
761
- def load_file(pk):
764
+ def _load_file_from_proxy(pk):
762
765
  """Load a file proxy stored in the database by primary key.
763
766
 
764
767
  Args:
765
768
  pk (uuid): Primary key of the `FileProxy` to retrieve
766
769
 
767
770
  Returns:
768
- (FileProxy): A File-like object
771
+ (File): A File-like object
769
772
  """
770
773
  fp = FileProxy.objects.get(pk=pk)
771
774
  return fp.file
772
775
 
773
776
  @staticmethod
774
- def save_file(uploaded_file):
777
+ def _save_file_to_proxy(uploaded_file):
775
778
  """
776
779
  Save an uploaded file to the database as a file proxy and return the
777
780
  primary key.
@@ -785,7 +788,7 @@ class BaseJob(Task):
785
788
  fp = FileProxy.objects.create(name=uploaded_file.name, file=uploaded_file)
786
789
  return fp.pk
787
790
 
788
- def delete_files(self, *files_to_delete):
791
+ def _delete_file_proxies(self, *files_to_delete):
789
792
  """Given an unpacked list of primary keys for `FileProxy` objects, delete them.
790
793
 
791
794
  Args:
@@ -824,6 +827,25 @@ class BaseJob(Task):
824
827
 
825
828
  return data
826
829
 
830
+ def create_file(self, filename, content):
831
+ """
832
+ Create a file that can later be downloaded by users.
833
+
834
+ Args:
835
+ filename (str): Name of the file to create, including extension
836
+ content (str, bytes): Content to populate the created file with.
837
+
838
+ Returns:
839
+ (FileProxy): record that was created
840
+ """
841
+ if isinstance(content, str):
842
+ content = content.encode("utf-8")
843
+ fp = FileProxy.objects.create(
844
+ name=filename, job_result=self.job_result, file=ContentFile(content, name=filename)
845
+ )
846
+ self.logger.info("Created file [%s](%s)", filename, fp.file.url)
847
+ return fp
848
+
827
849
 
828
850
  class Job(BaseJob):
829
851
  """
@@ -0,0 +1,32 @@
1
+ # Generated by Django 3.2.22 on 2023-10-27 16:32
2
+
3
+ from django.db import migrations, models
4
+ import django.db.models.deletion
5
+ import nautobot.extras.models.models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+ dependencies = [
10
+ ("extras", "0099_remove_dangling_note_objects"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name="fileproxy",
16
+ name="job_result",
17
+ field=models.ForeignKey(
18
+ blank=True,
19
+ null=True,
20
+ on_delete=django.db.models.deletion.CASCADE,
21
+ related_name="files",
22
+ to="extras.jobresult",
23
+ ),
24
+ ),
25
+ migrations.AlterField(
26
+ model_name="fileproxy",
27
+ name="file",
28
+ field=models.FileField(
29
+ storage=nautobot.extras.models.models._job_storage, upload_to=nautobot.extras.models.models._upload_to
30
+ ),
31
+ ),
32
+ ]
@@ -0,0 +1,61 @@
1
+ # Generated by Django 3.2.22 on 2023-11-01 21:29
2
+
3
+ import django.core.serializers.json
4
+ import django.core.validators
5
+ from django.db import migrations, models
6
+ import django.db.models.deletion
7
+ import nautobot.core.models.fields
8
+ import nautobot.core.models.validators
9
+ import nautobot.extras.models.mixins
10
+ import uuid
11
+
12
+
13
+ class Migration(migrations.Migration):
14
+ dependencies = [
15
+ ("extras", "0100_fileproxy_job_result"),
16
+ ]
17
+
18
+ operations = [
19
+ migrations.CreateModel(
20
+ name="ExternalIntegration",
21
+ fields=[
22
+ (
23
+ "id",
24
+ models.UUIDField(
25
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True
26
+ ),
27
+ ),
28
+ ("created", models.DateTimeField(auto_now_add=True, null=True)),
29
+ ("last_updated", models.DateTimeField(auto_now=True, null=True)),
30
+ (
31
+ "_custom_field_data",
32
+ models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
33
+ ),
34
+ ("name", models.CharField(max_length=255, unique=True)),
35
+ (
36
+ "remote_url",
37
+ models.CharField(
38
+ max_length=500, validators=[nautobot.core.models.validators.EnhancedURLValidator()]
39
+ ),
40
+ ),
41
+ ("verify_ssl", models.BooleanField(default=True)),
42
+ ("timeout", models.IntegerField(default=30, validators=[django.core.validators.MinValueValidator(0)])),
43
+ ("extra_config", models.JSONField(blank=True, null=True)),
44
+ (
45
+ "secrets_group",
46
+ models.ForeignKey(
47
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to="extras.secretsgroup"
48
+ ),
49
+ ),
50
+ ("tags", nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag")),
51
+ ],
52
+ options={
53
+ "ordering": ["name"],
54
+ },
55
+ bases=(
56
+ models.Model,
57
+ nautobot.extras.models.mixins.DynamicGroupMixin,
58
+ nautobot.extras.models.mixins.NotesMixin,
59
+ ),
60
+ ),
61
+ ]
@@ -0,0 +1,32 @@
1
+ # Generated by Django 3.2.23 on 2023-11-27 20:43
2
+
3
+ from django.db import migrations, models
4
+ import django.db.models.deletion
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+ dependencies = [
9
+ ("contenttypes", "0002_remove_content_type_name"),
10
+ ("extras", "0101_externalintegration"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name="objectchange",
16
+ name="changed_object_type",
17
+ field=models.ForeignKey(
18
+ null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="contenttypes.contenttype"
19
+ ),
20
+ ),
21
+ migrations.AlterField(
22
+ model_name="objectchange",
23
+ name="related_object_type",
24
+ field=models.ForeignKey(
25
+ blank=True,
26
+ null=True,
27
+ on_delete=django.db.models.deletion.SET_NULL,
28
+ related_name="+",
29
+ to="contenttypes.contenttype",
30
+ ),
31
+ ),
32
+ ]
@@ -19,6 +19,7 @@ from .models import (
19
19
  ConfigContextSchema,
20
20
  CustomLink,
21
21
  ExportTemplate,
22
+ ExternalIntegration,
22
23
  FileAttachment,
23
24
  FileProxy,
24
25
  GraphQLQuery,
@@ -45,6 +46,7 @@ __all__ = (
45
46
  "DynamicGroup",
46
47
  "DynamicGroupMembership",
47
48
  "ExportTemplate",
49
+ "ExternalIntegration",
48
50
  "FileAttachment",
49
51
  "FileProxy",
50
52
  "GitRepository",
@@ -84,7 +84,7 @@ class ObjectChange(BaseModel):
84
84
  user_name = models.CharField(max_length=150, editable=False)
85
85
  request_id = models.UUIDField(editable=False, db_index=True)
86
86
  action = models.CharField(max_length=50, choices=ObjectChangeActionChoices)
87
- changed_object_type = models.ForeignKey(to=ContentType, on_delete=models.PROTECT, related_name="+")
87
+ changed_object_type = models.ForeignKey(to=ContentType, on_delete=models.SET_NULL, null=True, related_name="+")
88
88
  changed_object_id = models.UUIDField(db_index=True)
89
89
  changed_object = GenericForeignKey(ct_field="changed_object_type", fk_field="changed_object_id")
90
90
  change_context = models.CharField(
@@ -96,7 +96,7 @@ class ObjectChange(BaseModel):
96
96
  change_context_detail = models.CharField(max_length=CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL, blank=True, editable=False)
97
97
  related_object_type = models.ForeignKey(
98
98
  to=ContentType,
99
- on_delete=models.PROTECT,
99
+ on_delete=models.SET_NULL,
100
100
  related_name="+",
101
101
  blank=True,
102
102
  null=True,