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
@@ -5,7 +5,6 @@ from nautobot.core.apps import (
5
5
  NavMenuAddButton,
6
6
  NavMenuGroup,
7
7
  NavMenuItem,
8
- NavMenuImportButton,
9
8
  NavMenuTab,
10
9
  )
11
10
 
@@ -33,12 +32,6 @@ menu_items = (
33
32
  "circuits.add_circuit",
34
33
  ],
35
34
  ),
36
- NavMenuImportButton(
37
- link="circuits:circuit_import",
38
- permissions=[
39
- "circuits.add_circuit",
40
- ],
41
- ),
42
35
  ),
43
36
  ),
44
37
  NavMenuItem(
@@ -55,12 +48,6 @@ menu_items = (
55
48
  "circuits.add_circuittype",
56
49
  ],
57
50
  ),
58
- NavMenuImportButton(
59
- link="circuits:circuittype_import",
60
- permissions=[
61
- "circuits.add_circuittype",
62
- ],
63
- ),
64
51
  ),
65
52
  ),
66
53
  ),
@@ -83,12 +70,6 @@ menu_items = (
83
70
  "circuits.add_provider",
84
71
  ],
85
72
  ),
86
- NavMenuImportButton(
87
- link="circuits:provider_import",
88
- permissions=[
89
- "circuits.add_provider",
90
- ],
91
- ),
92
73
  ),
93
74
  ),
94
75
  NavMenuItem(
@@ -105,12 +86,6 @@ menu_items = (
105
86
  "circuits.add_providernetwork",
106
87
  ],
107
88
  ),
108
- NavMenuImportButton(
109
- link="circuits:providernetwork_import",
110
- permissions=[
111
- "circuits.add_providernetwork",
112
- ],
113
- ),
114
89
  ),
115
90
  ),
116
91
  ),
@@ -5,15 +5,6 @@
5
5
  <li><a href="{% url 'circuits:circuit_list' %}?provider={{ object.provider.pk }}">{{ object.provider }}</a></li>
6
6
  {% endblock extra_breadcrumbs %}
7
7
 
8
- {% block masthead %}
9
- <h1>
10
- <span class="hover_copy"><span id="copy_title">{{ object }}</span><button type="button" class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#copy_title">
11
- <span class="mdi mdi-content-copy"></span>
12
- </button>
13
- </span>
14
- </h1>
15
- {% endblock masthead %}
16
-
17
8
  {% block content_left_page %}
18
9
  <div class="panel panel-default">
19
10
  <div class="panel-heading">
@@ -9,8 +9,6 @@
9
9
  <li>{{ object }}</li>
10
10
  {% endblock %}
11
11
 
12
- {% block title %}{{ object.provider }} {{ object.name }}{% endblock title %}
13
-
14
12
  {% block content_left_page %}
15
13
  <div class="panel panel-default">
16
14
  <div class="panel-heading">
@@ -91,6 +91,7 @@ class CircuitTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFil
91
91
  ["provider_network", "circuit_terminations__provider_network__name"],
92
92
  ["circuit_type", "circuit_type__id"],
93
93
  ["circuit_type", "circuit_type__name"],
94
+ ["status", "status__id"],
94
95
  ["status", "status__name"],
95
96
  ["circuit_termination_a"],
96
97
  ["circuit_termination_z"],
@@ -134,7 +134,15 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
134
134
  natural_keys_values = None
135
135
  natural_slug = serializers.SerializerMethodField()
136
136
 
137
- def __init__(self, *args, **kwargs):
137
+ def __init__(self, *args, force_csv=False, **kwargs):
138
+ """
139
+ Instantiate a BaseModelSerializer.
140
+
141
+ The force_csv kwarg allows you to force _is_csv_request() to evaluate True without passing a Request object,
142
+ which is necessary to be able to export appropriately structured CSV from a Job that doesn't have a Request.
143
+ """
144
+ self._force_csv = force_csv
145
+
138
146
  super().__init__(*args, **kwargs)
139
147
  # If it is not a Nested Serializer, we should set the depth argument to whatever is in the request's context
140
148
  if not self.is_nested:
@@ -270,6 +278,8 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
270
278
 
271
279
  def _is_csv_request(self):
272
280
  """Return True if this a CSV export request"""
281
+ if self._force_csv:
282
+ return True
273
283
  request = self.context.get("request")
274
284
  return hasattr(request, "accepted_media_type") and "text/csv" in request.accepted_media_type
275
285
 
@@ -361,10 +371,10 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
361
371
  self.extend_field_names(fields, "id", at_start=True)
362
372
 
363
373
  # Move these fields to the end
364
- if hasattr(self.Meta.model, "created"):
365
- self.extend_field_names(fields, "created")
366
- if hasattr(self.Meta.model, "last_updated"):
367
- self.extend_field_names(fields, "last_updated")
374
+ fields_to_include = ["created", "last_updated"]
375
+ for field in fields_to_include:
376
+ if hasattr(self.Meta.model, field):
377
+ self.extend_field_names(fields, field)
368
378
 
369
379
  def filter_field(field):
370
380
  # Eliminate all field names that start with "_" as those fields are not user-facing
@@ -494,7 +494,7 @@ class NautobotSpectacularSwaggerView(APIVersioningGetSchemaURLMixin, Spectacular
494
494
  @extend_schema(exclude=True)
495
495
  def get(self, request, *args, **kwargs):
496
496
  """Fix up the rendering of the Swagger UI to work with Nautobot's UI."""
497
- if not request.user.is_authenticated and get_settings_or_config("HIDE_RESTRICTED_UI"):
497
+ if not request.user.is_authenticated:
498
498
  doc_url = reverse("api_docs")
499
499
  login_url = reverse(settings.LOGIN_URL)
500
500
  return redirect(f"{login_url}?next={doc_url}")
@@ -721,18 +721,16 @@ class GetMenuAPIView(NautobotAPIVersionMixin, APIView):
721
721
 
722
722
  permission_classes = [IsAuthenticated]
723
723
 
724
- def format_and_remove_hidden_menu(self, request, data, hide_restricted_ui):
724
+ def format_and_remove_hidden_menu(self, request, data):
725
725
  """
726
726
  Formats the menu data and removes hidden menu items based on user permissions.
727
727
 
728
728
  Args:
729
729
  request (HttpRequest): The request object.
730
- data (Union[dict, str]): The menu data to format and filter. Can be either a dictionary or a string.
731
- hide_restricted_ui (bool): Flag indicating whether to hide restricted menu items.
730
+ data (dict): The menu data to format and filter.
732
731
 
733
732
  Returns:
734
- (Union[dict, str]): The formatted menu data without hidden items. Returns a dict if `data` is a
735
- `dict`, otherwise returns a string.
733
+ (dict): The formatted menu data without hidden items.
736
734
 
737
735
  Example:
738
736
  Input:
@@ -747,15 +745,18 @@ class GetMenuAPIView(NautobotAPIVersionMixin, APIView):
747
745
  Output:
748
746
  {"Devices": "data value"}
749
747
  """
750
- if isinstance(data, dict):
751
- return_value = {}
752
- for name, value in data.items():
753
- if not hide_restricted_ui or any(
754
- request.user.has_perm(permission) for permission in value["permissions"]
755
- ):
756
- return_value[name] = self.format_and_remove_hidden_menu(request, value["data"], hide_restricted_ui)
757
- return return_value
758
- return data
748
+ return_value = {}
749
+ for name, value in data.items():
750
+ if "permissions" in value:
751
+ permissions = value["permissions"]
752
+ user_has_permission = any(request.user.has_perm(permission) for permission in permissions)
753
+ if user_has_permission:
754
+ return_value[name] = value["data"]
755
+ else:
756
+ return_data = self.format_and_remove_hidden_menu(request, value["data"])
757
+ if return_data:
758
+ return_value[name] = return_data
759
+ return return_value
759
760
 
760
761
  @extend_schema(exclude=True)
761
762
  def get(self, request):
@@ -796,8 +797,7 @@ class GetMenuAPIView(NautobotAPIVersionMixin, APIView):
796
797
  }
797
798
  """
798
799
  base_menu = registry["new_ui_nav_menu"]
799
- HIDE_RESTRICTED_UI = get_settings_or_config("HIDE_RESTRICTED_UI")
800
- formatted_data = self.format_and_remove_hidden_menu(request, base_menu, HIDE_RESTRICTED_UI)
800
+ formatted_data = self.format_and_remove_hidden_menu(request, base_menu)
801
801
  return Response(formatted_data)
802
802
 
803
803
 
@@ -838,13 +838,12 @@ class GetObjectCountsView(NautobotAPIVersionMixin, APIView):
838
838
  {"model": "extras.role"},
839
839
  ],
840
840
  }
841
- HIDE_RESTRICTED_UI = get_settings_or_config("HIDE_RESTRICTED_UI")
842
841
 
843
842
  for entry in itertools.chain(*object_counts.values()):
844
843
  app_label, model_name = entry["model"].split(".")
845
844
  model = apps.get_model(app_label, model_name)
846
845
  permission = get_permission_for_model(model, "view")
847
- if HIDE_RESTRICTED_UI and not request.user.has_perm(permission):
846
+ if not request.user.has_perm(permission):
848
847
  continue
849
848
  data = {"name": model._meta.verbose_name_plural}
850
849
  try:
nautobot/core/choices.py CHANGED
@@ -203,7 +203,7 @@ class ButtonActionColorChoices(ChoiceSet):
203
203
  Map standard button actions to Bootstrap color classes.
204
204
  """
205
205
 
206
- ADD = "success"
206
+ ADD = "info"
207
207
  CANCEL = "default"
208
208
  CLONE = "success"
209
209
  CONFIGURE = "default"
nautobot/core/filters.py CHANGED
@@ -630,15 +630,23 @@ class BaseFilterSet(django_filters.FilterSet):
630
630
  if field is None:
631
631
  return magic_filters
632
632
 
633
+ # If the field allows null values, add an `isnull`` check
634
+ if getattr(field, "null", None):
635
+ # Use this method vs extend as the `lookup_map` variable is generally one of
636
+ # the constants which we do not want to update
637
+ lookup_map = dict(lookup_map, **{"isnull": "isnull"})
638
+
633
639
  # Create new filters for each lookup expression in the map
634
640
  for lookup_name, lookup_expr in lookup_map.items():
635
641
  new_filter_name = f"{filter_name}__{lookup_name}"
636
642
 
637
643
  try:
638
- if filter_name in cls.declared_filters:
639
- # The filter field has been explicity defined on the filterset class so we must manually
644
+ if filter_name in cls.declared_filters and lookup_expr not in {"isnull"}:
645
+ # The filter field has been explicitly defined on the filterset class so we must manually
640
646
  # create the new filter with the same type because there is no guarantee the defined type
641
- # is the same as the default type for the field
647
+ # is the same as the default type for the field. This does not apply if the filter
648
+ # should retain the original lookup_expr type, such as `isnull` using a boolean field on a
649
+ # char or date object.
642
650
  resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid
643
651
  new_filter = type(filter_field)(
644
652
  field_name=field_name,
@@ -649,7 +657,7 @@ class BaseFilterSet(django_filters.FilterSet):
649
657
  **filter_field.extra,
650
658
  )
651
659
  else:
652
- # The filter field is listed in Meta.fields so we can safely rely on default behaviour
660
+ # The filter field is listed in Meta.fields so we can safely rely on default behavior
653
661
  # Will raise FieldLookupError if the lookup is invalid
654
662
  new_filter = cls.filter_for_field(field, field_name, lookup_expr)
655
663
  except django_filters.exceptions.FieldLookupError:
@@ -1,7 +1,16 @@
1
+ from django.conf import settings
2
+ from django.contrib.contenttypes.models import ContentType
3
+ from django.core.exceptions import PermissionDenied
4
+ from django.http import QueryDict
5
+
6
+ from nautobot.core.api.renderers import NautobotCSVRenderer
7
+ from nautobot.core.api.utils import get_serializer_for_model
1
8
  from nautobot.core.celery import app, register_jobs
9
+ from nautobot.core.utils.lookup import get_filterset_for_model
10
+ from nautobot.core.utils.requests import get_filterable_params_from_filter_params
2
11
  from nautobot.extras.datasources import ensure_git_repository, git_repository_dry_run, refresh_datasource_content
3
- from nautobot.extras.jobs import Job, ObjectVar
4
- from nautobot.extras.models import GitRepository
12
+ from nautobot.extras.jobs import ChoiceVar, Job, ObjectVar, RunJobTaskFailed, StringVar
13
+ from nautobot.extras.models import ExportTemplate, GitRepository
5
14
 
6
15
  name = "System Jobs"
7
16
 
@@ -59,5 +68,118 @@ class GitRepositoryDryRun(Job):
59
68
  self.logger.info(f"Repository dry run completed in {job_result.duration}")
60
69
 
61
70
 
62
- jobs = [GitRepositorySync, GitRepositoryDryRun]
71
+ class ExportObjectList(Job):
72
+ """System Job to export a list of objects via CSV or ExportTemplate."""
73
+
74
+ content_type = ObjectVar(
75
+ model=ContentType,
76
+ description="Type of objects to export",
77
+ label="Content Type",
78
+ )
79
+ query_string = StringVar(
80
+ description='Filterset parameters to apply, in URL query parameter format e.g. "name=test&status=Active"',
81
+ label="Filterset Parameters",
82
+ default="",
83
+ required=False,
84
+ )
85
+ export_format = ChoiceVar(
86
+ choices=(("csv", "CSV"), ("yaml", "YAML")),
87
+ description="Format to export to if not using an Export Template<br>"
88
+ "(note, in core only <code>dcim | device type</code> supports YAML export at present)",
89
+ default="csv",
90
+ required=False,
91
+ )
92
+ export_template = ObjectVar(
93
+ model=ExportTemplate,
94
+ query_params={"content_type": "$content_type"},
95
+ display_field="name",
96
+ description="Export Template to use (if unspecified, will export to CSV/YAML as specified above)",
97
+ label="Export Template",
98
+ default=None,
99
+ required=False,
100
+ )
101
+
102
+ class Meta:
103
+ name = "Export Object List"
104
+ has_sensitive_variables = False
105
+ # Exporting large querysets may take substantial processing time
106
+ soft_time_limit = 1800
107
+ time_limit = 2000
108
+
109
+ def run(self, *, content_type, query_string="", export_format="csv", export_template=None):
110
+ if not self.user.has_perm(f"{content_type.app_label}.view_{content_type.model}"):
111
+ self.logger.error('User "%s" does not have permission to view %s objects', self.user, content_type.model)
112
+ raise PermissionDenied("User does not have view permissions on the requested content-type")
113
+
114
+ model = content_type.model_class()
115
+
116
+ # Start with all objects of the requested type
117
+ queryset = model.objects.all()
118
+ # Enforce user permissions
119
+ queryset = queryset.restrict(self.user, "view")
120
+
121
+ # Filter the queryset based on the provided query_string
122
+ filterset_class = get_filterset_for_model(model)
123
+ self.logger.debug("Found filterset class: `%s`", filterset_class.__name__)
124
+ # TODO: ideally the ObjectListView should strip its non_filter_params (which may vary by view!)
125
+ # such that they never are even seen here.
126
+ query_params = QueryDict(query_string)
127
+ self.logger.debug("Parsed query_params: `%s`", query_params.dict())
128
+ default_non_filter_params = ("export", "page", "per_page", "sort")
129
+ filter_params = get_filterable_params_from_filter_params(
130
+ query_params, default_non_filter_params, filterset_class()
131
+ )
132
+ self.logger.debug("Filterset params: `%s`", filter_params)
133
+ filterset = filterset_class(filter_params, queryset)
134
+ if not filterset.is_valid():
135
+ self.logger.error("Invalid filters were specified: %s", filterset.errors)
136
+ raise RunJobTaskFailed("Invalid query_string value for this content_type")
137
+ queryset = filterset.qs
138
+ object_count = queryset.count()
139
+
140
+ filename = f"{settings.BRANDING_PREPENDED_FILENAME}{model._meta.verbose_name_plural.lower().replace(' ', '_')}"
141
+
142
+ if export_template is not None:
143
+ # Export templates
144
+ if export_template.content_type != content_type:
145
+ self.logger.error("ExportTemplate %s doesn't apply to %s", export_template, content_type)
146
+ raise RunJobTaskFailed("ExportTemplate ContentType mismatch")
147
+ self.logger.info(
148
+ "Exporting %d objects via ExportTemplate. This may take some time.",
149
+ object_count,
150
+ extra={"object": export_template},
151
+ )
152
+ try:
153
+ # export_template.render() consumes the whole queryset, so we don't have any way to do a progress bar.
154
+ output = export_template.render(queryset)
155
+ except Exception as err:
156
+ self.logger.error("Error when rendering ExportTemplate: %s", err)
157
+ raise
158
+ if export_template.file_extension:
159
+ filename += f".{export_template.file_extension}"
160
+ self.create_file(filename, output)
161
+
162
+ elif export_format == "yaml":
163
+ # Device-type (etc.) YAML export
164
+ if not hasattr(model, "to_yaml"):
165
+ self.logger.error("Model %s doesn't support YAML export", content_type.model)
166
+ raise ValueError("YAML export not supported for this content-type")
167
+ self.logger.info("Exporting %d objects to YAML. This may take some time.", object_count)
168
+ yaml_data = [obj.to_yaml() for obj in queryset]
169
+ self.create_file(filename + ".yaml", "---\n".join(yaml_data))
170
+
171
+ else:
172
+ # Generic CSV export
173
+ serializer_class = get_serializer_for_model(model)
174
+ self.logger.debug("Found serializer class: `%s`", serializer_class.__name__)
175
+ renderer = NautobotCSVRenderer()
176
+ self.logger.info("Exporting %d objects to CSV. This may take some time.", object_count)
177
+ # The force_csv=True attribute is a hack, but much easier than trying to construct a valid HttpRequest
178
+ # object from scratch that passes all implicit and explicit assumptions in Django and DRF.
179
+ serializer = serializer_class(queryset, many=True, context={"request": None}, force_csv=True)
180
+ csv_data = renderer.render(serializer.data)
181
+ self.create_file(filename + ".csv", csv_data)
182
+
183
+
184
+ jobs = [ExportObjectList, GitRepositorySync, GitRepositoryDryRun]
63
185
  register_jobs(*jobs)
@@ -63,7 +63,7 @@ class Command(BaseCommand):
63
63
  ManufacturerFactory,
64
64
  PlatformFactory,
65
65
  )
66
- from nautobot.extras.factory import RoleFactory, StatusFactory, TagFactory
66
+ from nautobot.extras.factory import ExternalIntegrationFactory, RoleFactory, StatusFactory, TagFactory
67
67
  from nautobot.extras.management import populate_status_choices
68
68
  from nautobot.dcim.factory import (
69
69
  LocationTypeFactory,
@@ -172,6 +172,9 @@ class Command(BaseCommand):
172
172
  has_description=True,
173
173
  using=db_name,
174
174
  )
175
+ self.stdout.write("Creating ExternalIntegrations...")
176
+ ExternalIntegrationFactory.create_batch(20, using=db_name)
177
+ # make sure we have some tenants that have null relationships to make filter tests happy
175
178
  self.stdout.write("Creating Tenants without Circuits, Locations, IPAddresses, or Prefixes...")
176
179
  TenantFactory.create_batch(10, using=db_name)
177
180
  # TODO: nautobot.tenancy.tests.test_filters currently calls the following additional factories:
@@ -1,7 +1,6 @@
1
1
  import json
2
2
  import re
3
3
 
4
- from django.contrib.contenttypes.models import ContentType
5
4
  from django.core import exceptions
6
5
  from django.core.validators import RegexValidator, MaxLengthValidator
7
6
  from django.db import models
@@ -187,7 +186,18 @@ class ForeignKeyLimitedByContentTypes(ForeignKeyWithAutoRelatedName):
187
186
  """
188
187
 
189
188
  def get_limit_choices_to(self):
190
- return {"content_types": ContentType.objects.get_for_model(self.model)}
189
+ """
190
+ Limit this field to only objects which are assigned to this model's content-type.
191
+
192
+ Note that this is implemented via specifying `content_types__app_label=` and `content_types__model=`
193
+ rather than via the more obvious `content_types=ContentType.objects.get_for_model(self.model)`
194
+ because the latter approach would involve a database query, and in some cases
195
+ (most notably FilterSet definition) this function is called **before** database migrations can be run.
196
+ """
197
+ return {
198
+ "content_types__app_label": self.model._meta.app_label,
199
+ "content_types__model": self.model._meta.model_name,
200
+ }
191
201
 
192
202
  def formfield(self, **kwargs):
193
203
  """Return a prepped formfield for use in model forms."""
nautobot/core/settings.py CHANGED
@@ -87,6 +87,9 @@ HTTP_PROXIES = None
87
87
  # Send anonymized installation metrics when post_upgrade or send_installation_metrics management commands are run
88
88
  INSTALLATION_METRICS_ENABLED = is_truthy(os.getenv("NAUTOBOT_INSTALLATION_METRICS_ENABLED", "True"))
89
89
 
90
+ # The storage backend to use for Job input files and Job output files
91
+ JOB_FILE_IO_STORAGE = os.getenv("NAUTOBOT_JOB_FILE_IO_STORAGE", "db_file_storage.storage.DatabaseFileStorage")
92
+
90
93
  # The file path to a directory where locally installed Jobs can be discovered
91
94
  JOBS_ROOT = os.getenv("NAUTOBOT_JOBS_ROOT", os.path.join(NAUTOBOT_ROOT, "jobs").rstrip("/"))
92
95
 
@@ -611,12 +614,6 @@ CONSTANCE_CONFIG = {
611
614
  help_text="Whether to show the Feedback button in the new UI sidebar.",
612
615
  field_type=bool,
613
616
  ),
614
- "HIDE_RESTRICTED_UI": ConstanceConfigItem(
615
- default=False,
616
- help_text="If set to True, users with limited permissions will not be shown menu items and home-page elements that "
617
- "they do not have permission to access.",
618
- field_type=bool,
619
- ),
620
617
  "LOCATION_NAME_AS_NATURAL_KEY": ConstanceConfigItem(
621
618
  default=False,
622
619
  help_text="Location names are not guaranteed globally-unique by Nautobot but in practice they often are. "
@@ -700,7 +697,7 @@ CONSTANCE_CONFIG_FIELDSETS = {
700
697
  "Performance": ["DYNAMIC_GROUPS_MEMBER_CACHE_TIMEOUT"],
701
698
  "Rack Elevation Rendering": ["RACK_ELEVATION_DEFAULT_UNIT_HEIGHT", "RACK_ELEVATION_DEFAULT_UNIT_WIDTH"],
702
699
  "Release Checking": ["RELEASE_CHECK_URL", "RELEASE_CHECK_TIMEOUT"],
703
- "User Interface": ["HIDE_RESTRICTED_UI", "FEEDBACK_BUTTON_ENABLED", "SUPPORT_MESSAGE"],
700
+ "User Interface": ["FEEDBACK_BUTTON_ENABLED", "SUPPORT_MESSAGE"],
704
701
  }
705
702
 
706
703
  #
@@ -839,6 +836,10 @@ BRANDING_FILEPATHS = {
839
836
  "icon_mask": os.getenv(
840
837
  "NAUTOBOT_BRANDING_FILEPATHS_ICON_MASK", None
841
838
  ), # mono-chrome icon used for the mask-icon header
839
+ "header_bullet": os.getenv(
840
+ "NAUTOBOT_BRANDING_FILEPATHS_HEADER_BULLET", None
841
+ ), # bullet image used for various view headers
842
+ "nav_bullet": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_NAV_BULLET", None), # bullet image used for nav menu headers
842
843
  }
843
844
 
844
845
  # Title to use in place of "Nautobot"
@@ -12,7 +12,7 @@
12
12
  <body>
13
13
  {% include 'inc/nav_menu.html' %}
14
14
  <div class="container-fluid wrapper">
15
- {% if request.user.is_authenticated or not "HIDE_RESTRICTED_UI"|settings_or_config %}
15
+ {% if request.user.is_authenticated %}
16
16
  {% if "BANNER_TOP"|settings_or_config %}
17
17
  <div class="alert alert-info text-center" role="alert">
18
18
  {{ "BANNER_TOP"|settings_or_config|safe }}
@@ -37,7 +37,7 @@
37
37
  {% block header %}{% endblock header %}
38
38
  {% block content %}{% endblock content %}
39
39
  <div class="push"></div>
40
- {% if request.user.is_authenticated or not "HIDE_RESTRICTED_UI"|settings_or_config %}
40
+ {% if request.user.is_authenticated %}
41
41
  {% if "BANNER_BOTTOM"|settings_or_config %}
42
42
  <div class="alert alert-info text-center banner-bottom" role="alert">
43
43
  {{ "BANNER_BOTTOM"|settings_or_config|safe }}
@@ -1,33 +1,60 @@
1
- {% if export_templates %}
2
- <div class="btn-group">
3
- <button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
4
- <span class="mdi mdi-database-export" aria-hidden="true"></span>
5
- Export <span class="caret"></span>
6
- </button>
7
- <ul class="dropdown-menu dropdown-menu-right">
8
- {% if export_url %}
1
+ {% if export_url %}
2
+ {% if export_templates or include_yaml_option %}
3
+ <div class="btn-group">
4
+ <button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
5
+ <span class="mdi mdi-database-export" aria-hidden="true"></span>
6
+ Export <span class="caret"></span>
7
+ </button>
8
+ <ul class="dropdown-menu dropdown-menu-right">
9
9
  <li>
10
- <a href="{{ export_url }}?{% if url_params %}{{ url_params.urlencode }}&{% endif %}format=csv">
11
- Default format
12
- </a>
10
+ <form action="{{ export_url }}" method="post">
11
+ {% csrf_token %}
12
+ <input type="hidden" name="content_type" value="{{ content_type.pk }}">
13
+ <input type="hidden" name="query_string" value="{{ query_string }}">
14
+ <input type="hidden" name="_schedule_type" value="immediately">
15
+ <button type="submit" class="btn btn-link">CSV format</button>
16
+ </form>
13
17
  </li>
14
- {% endif %}
15
- <li class="divider"></li>
16
- {% for et in export_templates %}
17
- <li>
18
- <a href="?{% if url_params %}{{ url_params.urlencode }}&{% endif %}export={{ et.name }}"
19
- {% if et.description %} title="{{ et.description }}"{% endif %}
20
- >
21
- {{ et.name }}
22
- </a>
23
- </li>
24
- {% endfor %}
25
- </ul>
26
- </div>
27
- {% elif export_url %}
28
- <a href="{{ export_url }}?{% if url_params %}{{ url_params.urlencode }}&{% endif %}format=csv"
29
- class="btn btn-success"
30
- >
31
- <span class="mdi mdi-database-export" aria-hidden="true"></span> Export
32
- </a>
18
+ {% if include_yaml_option %}
19
+ <li>
20
+ <form action="{{ export_url }}" method="post">
21
+ {% csrf_token %}
22
+ <input type="hidden" name="content_type" value="{{ content_type.pk }}">
23
+ <input type="hidden" name="query_string" value="{{ query_string }}">
24
+ <input type="hidden" name="export_format" value="yaml">
25
+ <input type="hidden" name="_schedule_type" value="immediately">
26
+ <button type="submit" class="btn btn-link">YAML format</button>
27
+ </form>
28
+ </li>
29
+ {% endif %}
30
+ {% if export_templates %}
31
+ <li class="divider"></li>
32
+ {% for et in export_templates %}
33
+ <li>
34
+ <form action="{{ export_url }}" method="post">
35
+ {% csrf_token %}
36
+ <input type="hidden" name="content_type" value="{{ content_type.pk }}">
37
+ <input type="hidden" name="export_template" value="{{ et.pk }}">
38
+ <input type="hidden" name="query_string" value="{{ query_string }}">
39
+ <input type="hidden" name="_schedule_type" value="immediately">
40
+ <button type="submit" class="btn btn-link"
41
+ {% if et.description %}title="{{ et.description }}"{% endif %}
42
+ >{{ et.name }}</button>
43
+ </form>
44
+ </li>
45
+ {% endfor %}
46
+ {% endif %}
47
+ </ul>
48
+ </div>
49
+ {% else %}
50
+ <form style="display: inline-block" action="{{ export_url }}" method="post">
51
+ {% csrf_token %}
52
+ <input type="hidden" name="content_type" value="{{ content_type.pk }}">
53
+ <input type="hidden" name="query_string" value="{{ query_string }}">
54
+ <input type="hidden" name="_schedule_type" value="immediately">
55
+ <button type="submit" class="btn btn-success">
56
+ <span class="mdi mdi-database-export" aria-hidden="true"></span> Export
57
+ </button>
58
+ </form>
59
+ {% endif %}
33
60
  {% endif %}
@@ -126,7 +126,7 @@
126
126
  </div>
127
127
  </div>
128
128
  {% endif %}
129
- {% include table_template|default:'responsive_table.html' %}
129
+ {% include table_template|default:'panel_table.html' %}
130
130
  <div class="pull-left noprint">
131
131
  {% block bulk_buttons %}{% endblock %}
132
132
  {% if bulk_edit_url and permissions.change %}
@@ -142,7 +142,7 @@
142
142
  </div>
143
143
  </form>
144
144
  {% else %}
145
- {% include table_template|default:'responsive_table.html' %}
145
+ {% include table_template|default:'panel_table.html' %}
146
146
  {% endif %}
147
147
  {% endwith %}
148
148
  {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
@@ -58,7 +58,14 @@
58
58
  </div>
59
59
 
60
60
  {% block masthead %}
61
- <h1>{% block title %}{{ object }}{% endblock %}</h1>
61
+ <h1>
62
+ <span class="hover_copy">
63
+ <span id="copy_title">{% block title %}{{object.display|default:object}}{% endblock %}</span>
64
+ <button type="button" class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#copy_title">
65
+ <span class="mdi mdi-content-copy"></span>
66
+ </button>
67
+ </span>
68
+ </h1>
62
69
  {% endblock masthead %}
63
70
  {% include 'inc/created_updated.html' %}
64
71
  <div class="pull-right noprint">