nautobot 2.2.5__py3-none-any.whl → 2.2.7__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 (345) hide show
  1. nautobot/apps/api.py +2 -0
  2. nautobot/apps/models.py +2 -0
  3. nautobot/core/api/fields.py +13 -0
  4. nautobot/core/api/mixins.py +6 -1
  5. nautobot/core/api/schema.py +3 -1
  6. nautobot/core/api/serializers.py +7 -1
  7. nautobot/core/celery/__init__.py +1 -1
  8. nautobot/core/management/commands/generate_test_data.py +128 -158
  9. nautobot/core/models/fields.py +15 -0
  10. nautobot/core/tests/runner.py +10 -0
  11. nautobot/core/tests/test_utils.py +48 -1
  12. nautobot/core/utils/git.py +121 -49
  13. nautobot/core/utils/module_loading.py +10 -2
  14. nautobot/dcim/factory.py +1 -1
  15. nautobot/dcim/tests/test_models.py +2 -0
  16. nautobot/extras/datasources/git.py +133 -135
  17. nautobot/extras/datasources/utils.py +3 -0
  18. nautobot/extras/filters/__init__.py +1 -0
  19. nautobot/extras/forms/forms.py +16 -3
  20. nautobot/extras/jobs.py +9 -1
  21. nautobot/extras/migrations/0107_laxurlfield.py +28 -0
  22. nautobot/extras/migrations/0108_jobbutton_enabled.py +17 -0
  23. nautobot/extras/models/datasources.py +6 -4
  24. nautobot/extras/models/groups.py +9 -2
  25. nautobot/extras/models/jobs.py +30 -0
  26. nautobot/extras/models/models.py +2 -4
  27. nautobot/extras/tables.py +3 -0
  28. nautobot/extras/templates/extras/jobbutton_retrieve.html +6 -2
  29. nautobot/extras/templatetags/job_buttons.py +2 -2
  30. nautobot/extras/tests/git_data/01-valid-files/__init__.py +0 -0
  31. nautobot/extras/tests/git_data/01-valid-files/config_context_schemas/schema-1.yaml +18 -0
  32. nautobot/extras/tests/git_data/01-valid-files/config_contexts/context.yaml +12 -0
  33. nautobot/extras/tests/git_data/01-valid-files/config_contexts/devices/test-device.json +3 -0
  34. nautobot/extras/tests/git_data/01-valid-files/config_contexts/locations/Test Location.json +7 -0
  35. nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template.j2 +3 -0
  36. nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template2.html +4 -0
  37. nautobot/extras/tests/git_data/01-valid-files/export_templates/ipam/vlan/template.j2 +3 -0
  38. nautobot/extras/tests/git_data/01-valid-files/jobs/__init__.py +5 -0
  39. nautobot/extras/tests/git_data/01-valid-files/jobs/my_job.py +16 -0
  40. nautobot/extras/tests/git_data/02-invalid-files/__init__.py +0 -0
  41. nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema1.json +2 -0
  42. nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema2.json +1 -0
  43. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext1.json +2 -0
  44. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext2.json +1 -0
  45. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext3.json +3 -0
  46. nautobot/extras/tests/git_data/02-invalid-files/config_contexts/devices/nosuchdevice.json +1 -0
  47. nautobot/extras/tests/git_data/02-invalid-files/dcim/template.j2 +0 -0
  48. nautobot/extras/tests/git_data/02-invalid-files/devices/template.j2 +0 -0
  49. nautobot/extras/tests/git_data/02-invalid-files/export_templates/dcim/nosuchmodel/template.j2 +3 -0
  50. nautobot/extras/tests/git_data/02-invalid-files/export_templates/nosuchapp/device/template.j2 +3 -0
  51. nautobot/extras/tests/git_data/02-invalid-files/jobs/__init__.py +2 -0
  52. nautobot/extras/tests/git_data/02-invalid-files/jobs/importerror.py +1 -0
  53. nautobot/extras/tests/git_data/02-invalid-files/jobs/syntaxerror.py +1 -0
  54. nautobot/extras/tests/git_helper.py +76 -0
  55. nautobot/extras/tests/test_api.py +28 -11
  56. nautobot/extras/tests/test_datasources.py +94 -276
  57. nautobot/extras/tests/test_dynamicgroups.py +8 -1
  58. nautobot/extras/tests/test_models.py +8 -3
  59. nautobot/extras/tests/test_views.py +21 -7
  60. nautobot/extras/views.py +1 -1
  61. nautobot/ipam/api/serializers.py +46 -16
  62. nautobot/ipam/api/views.py +29 -16
  63. nautobot/ipam/filters.py +9 -1
  64. nautobot/ipam/forms.py +8 -0
  65. nautobot/ipam/tables.py +1 -1
  66. nautobot/ipam/tests/test_api.py +15 -20
  67. nautobot/ipam/tests/test_filters.py +15 -0
  68. nautobot/project-static/docs/404.html +83 -8
  69. nautobot/project-static/docs/apps/index.html +96 -10
  70. nautobot/project-static/docs/apps/nautobot-apps.html +96 -10
  71. nautobot/project-static/docs/assets/app-icons/icon-CapacityMetrics.svg +1 -0
  72. nautobot/project-static/docs/assets/app-icons/icon-CircuitMaintenance.png +0 -0
  73. nautobot/project-static/docs/assets/javascripts/{bundle.ebd0bdb7.min.js → bundle.fe8b6f2b.min.js} +4 -4
  74. nautobot/project-static/docs/assets/javascripts/{bundle.ebd0bdb7.min.js.map → bundle.fe8b6f2b.min.js.map} +3 -3
  75. nautobot/project-static/docs/assets/javascripts/glightbox.min.js +1 -0
  76. nautobot/project-static/docs/assets/stylesheets/glightbox.min.css +1 -0
  77. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +96 -10
  78. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +96 -10
  79. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +96 -10
  80. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +96 -10
  81. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +96 -10
  82. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +96 -10
  83. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +96 -10
  84. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +96 -10
  85. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +96 -10
  86. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +96 -10
  87. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +96 -10
  88. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +96 -10
  89. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +96 -10
  90. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +96 -10
  91. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +156 -12
  92. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +96 -10
  93. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +96 -10
  94. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +96 -10
  95. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +106 -11
  96. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +96 -10
  97. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +96 -10
  98. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +143 -11
  99. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +96 -10
  100. nautobot/project-static/docs/development/apps/api/configuration-view.html +96 -10
  101. nautobot/project-static/docs/development/apps/api/database-backend-config.html +96 -10
  102. nautobot/project-static/docs/development/apps/api/models/django-admin.html +96 -10
  103. nautobot/project-static/docs/development/apps/api/models/global-search.html +96 -10
  104. nautobot/project-static/docs/development/apps/api/models/graphql.html +96 -10
  105. nautobot/project-static/docs/development/apps/api/models/index.html +96 -10
  106. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +96 -10
  107. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +96 -10
  108. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +96 -10
  109. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +96 -10
  110. nautobot/project-static/docs/development/apps/api/platform-features/index.html +96 -10
  111. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +96 -10
  112. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +96 -10
  113. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +96 -10
  114. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +96 -10
  115. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +96 -10
  116. nautobot/project-static/docs/development/apps/api/prometheus.html +96 -10
  117. nautobot/project-static/docs/development/apps/api/setup.html +96 -10
  118. nautobot/project-static/docs/development/apps/api/testing.html +96 -10
  119. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +96 -10
  120. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +96 -10
  121. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +96 -10
  122. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +96 -10
  123. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +96 -10
  124. nautobot/project-static/docs/development/apps/api/views/base-template.html +96 -10
  125. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +96 -10
  126. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +96 -10
  127. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +96 -10
  128. nautobot/project-static/docs/development/apps/api/views/index.html +96 -10
  129. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +96 -10
  130. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +96 -10
  131. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +96 -10
  132. nautobot/project-static/docs/development/apps/api/views/notes.html +96 -10
  133. nautobot/project-static/docs/development/apps/api/views/rest-api.html +96 -10
  134. nautobot/project-static/docs/development/apps/api/views/urls.html +96 -10
  135. nautobot/project-static/docs/development/apps/index.html +96 -10
  136. nautobot/project-static/docs/development/apps/migration/code-updates.html +97 -11
  137. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +96 -10
  138. nautobot/project-static/docs/development/apps/migration/from-v1.html +96 -10
  139. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +96 -10
  140. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +96 -10
  141. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +96 -10
  142. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +96 -10
  143. nautobot/project-static/docs/development/apps/porting-from-netbox.html +96 -10
  144. nautobot/project-static/docs/development/core/application-registry.html +96 -10
  145. nautobot/project-static/docs/development/core/best-practices.html +96 -10
  146. nautobot/project-static/docs/development/core/bootstrap-ui.html +96 -10
  147. nautobot/project-static/docs/development/core/caching.html +96 -10
  148. nautobot/project-static/docs/development/core/controllers.html +96 -10
  149. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +96 -10
  150. nautobot/project-static/docs/development/core/generic-views.html +96 -10
  151. nautobot/project-static/docs/development/core/getting-started.html +98 -13
  152. nautobot/project-static/docs/development/core/homepage.html +96 -10
  153. nautobot/project-static/docs/development/core/index.html +96 -10
  154. nautobot/project-static/docs/development/core/model-checklist.html +96 -10
  155. nautobot/project-static/docs/development/core/model-features.html +96 -10
  156. nautobot/project-static/docs/development/core/natural-keys.html +96 -10
  157. nautobot/project-static/docs/development/core/navigation-menu.html +96 -10
  158. nautobot/project-static/docs/development/core/release-checklist.html +96 -10
  159. nautobot/project-static/docs/development/core/role-internals.html +96 -10
  160. nautobot/project-static/docs/development/core/settings.html +96 -10
  161. nautobot/project-static/docs/development/core/style-guide.html +96 -10
  162. nautobot/project-static/docs/development/core/templates.html +96 -10
  163. nautobot/project-static/docs/development/core/testing.html +109 -11
  164. nautobot/project-static/docs/development/core/user-preferences.html +96 -10
  165. nautobot/project-static/docs/development/index.html +96 -10
  166. nautobot/project-static/docs/development/jobs/index.html +96 -10
  167. nautobot/project-static/docs/development/jobs/migration/from-v1.html +96 -10
  168. nautobot/project-static/docs/index.html +13 -8362
  169. nautobot/project-static/docs/objects.inv +0 -0
  170. nautobot/project-static/docs/overview/application_stack.html +8229 -0
  171. nautobot/project-static/docs/overview/design_philosophy.html +8158 -0
  172. nautobot/project-static/docs/overview/index.html +8230 -0
  173. nautobot/project-static/docs/release-notes/index.html +96 -10
  174. nautobot/project-static/docs/release-notes/version-1.0.html +96 -10
  175. nautobot/project-static/docs/release-notes/version-1.1.html +96 -10
  176. nautobot/project-static/docs/release-notes/version-1.2.html +96 -10
  177. nautobot/project-static/docs/release-notes/version-1.3.html +96 -10
  178. nautobot/project-static/docs/release-notes/version-1.4.html +96 -10
  179. nautobot/project-static/docs/release-notes/version-1.5.html +96 -10
  180. nautobot/project-static/docs/release-notes/version-1.6.html +96 -10
  181. nautobot/project-static/docs/release-notes/version-2.0.html +96 -10
  182. nautobot/project-static/docs/release-notes/version-2.1.html +96 -10
  183. nautobot/project-static/docs/release-notes/version-2.2.html +516 -131
  184. nautobot/project-static/docs/requirements.txt +2 -1
  185. nautobot/project-static/docs/search/search_index.json +1 -1
  186. nautobot/project-static/docs/sitemap.xml +268 -258
  187. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  188. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +96 -10
  189. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +96 -10
  190. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +96 -10
  191. nautobot/project-static/docs/user-guide/administration/configuration/index.html +96 -10
  192. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +96 -10
  193. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +96 -10
  194. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +96 -10
  195. nautobot/project-static/docs/user-guide/administration/guides/caching.html +96 -10
  196. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +96 -10
  197. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +96 -10
  198. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +96 -10
  199. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +96 -10
  200. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +96 -10
  201. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +96 -10
  202. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +96 -10
  203. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +96 -10
  204. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +96 -10
  205. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +96 -10
  206. nautobot/project-static/docs/user-guide/administration/installation/index.html +96 -10
  207. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +96 -10
  208. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +96 -10
  209. nautobot/project-static/docs/user-guide/administration/installation/services.html +96 -10
  210. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +96 -10
  211. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +96 -10
  212. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +96 -10
  213. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +96 -10
  214. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +96 -10
  215. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +96 -10
  216. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +96 -10
  217. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +96 -10
  218. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +96 -10
  219. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +96 -10
  220. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +96 -10
  221. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +96 -10
  222. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +96 -10
  223. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +96 -10
  224. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-location-changes.yaml +1 -1
  225. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +97 -11
  226. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +96 -10
  227. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +96 -10
  228. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +96 -10
  229. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +96 -10
  230. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +96 -10
  231. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +96 -10
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +96 -10
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +96 -10
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +96 -10
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +96 -10
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +96 -10
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +96 -10
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +96 -10
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +96 -10
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +96 -10
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +96 -10
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +96 -10
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +96 -10
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +96 -10
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +96 -10
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +96 -10
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +96 -10
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +96 -10
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +96 -10
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +96 -10
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +96 -10
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +96 -10
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +96 -10
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +96 -10
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +96 -10
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +96 -10
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +96 -10
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +96 -10
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +96 -10
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +96 -10
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +96 -10
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +96 -10
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +96 -10
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +96 -10
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +96 -10
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +96 -10
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +96 -10
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +96 -10
  269. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +96 -10
  270. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +96 -10
  271. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +96 -10
  272. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +96 -10
  273. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +96 -10
  274. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +96 -10
  275. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +96 -10
  276. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +96 -10
  277. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +96 -10
  278. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +96 -10
  279. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +96 -10
  280. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +96 -10
  281. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +96 -10
  282. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +96 -10
  283. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +96 -10
  284. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +96 -10
  285. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +96 -10
  286. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +96 -10
  287. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +96 -10
  288. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +96 -10
  289. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +96 -10
  290. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +96 -10
  291. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +96 -10
  292. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +96 -10
  293. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +96 -10
  294. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +96 -10
  295. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +96 -10
  296. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +96 -10
  297. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +96 -10
  298. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +96 -10
  299. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +96 -10
  300. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +96 -10
  301. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +96 -10
  302. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +96 -10
  303. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +96 -10
  304. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +96 -10
  305. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +96 -10
  306. nautobot/project-static/docs/user-guide/index.html +99 -13
  307. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +96 -10
  308. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +96 -10
  309. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +96 -10
  310. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +96 -10
  311. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +96 -10
  312. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +96 -10
  313. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +96 -10
  314. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +96 -10
  315. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +96 -10
  316. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +96 -10
  317. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +96 -10
  318. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +97 -11
  319. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +96 -10
  320. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +101 -14
  321. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +96 -10
  322. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +96 -10
  323. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +96 -10
  324. nautobot/project-static/docs/user-guide/platform-functionality/note.html +96 -10
  325. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +96 -10
  326. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +96 -10
  327. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +96 -10
  328. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +96 -10
  329. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +96 -10
  330. nautobot/project-static/docs/user-guide/platform-functionality/role.html +96 -10
  331. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +96 -10
  332. nautobot/project-static/docs/user-guide/platform-functionality/status.html +96 -10
  333. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +96 -10
  334. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +96 -10
  335. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +96 -10
  336. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +96 -10
  337. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +96 -10
  338. nautobot/project-static/js/connection_toggles.js +7 -6
  339. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/METADATA +2 -2
  340. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/RECORD +344 -311
  341. nautobot/extras/tests/test_git.py +0 -23
  342. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/LICENSE.txt +0 -0
  343. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/NOTICE +0 -0
  344. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/WHEEL +0 -0
  345. {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/entry_points.txt +0 -0
@@ -27,6 +27,7 @@ from nautobot.core.forms import (
27
27
  DynamicModelMultipleChoiceField,
28
28
  JSONArrayFormField,
29
29
  JSONField,
30
+ LaxURLField,
30
31
  MultipleContentTypeField,
31
32
  SlugField,
32
33
  StaticSelect2,
@@ -687,7 +688,7 @@ class PasswordInputWithPlaceholder(forms.PasswordInput):
687
688
  class GitRepositoryForm(NautobotModelForm):
688
689
  slug = SlugField(help_text="Filesystem-friendly unique shorthand")
689
690
 
690
- remote_url = forms.URLField(
691
+ remote_url = LaxURLField(
691
692
  required=True,
692
693
  label="Remote URL",
693
694
  help_text="Only http:// and https:// URLs are presently supported",
@@ -738,7 +739,7 @@ class GitRepositoryBulkEditForm(NautobotBulkEditForm):
738
739
  queryset=GitRepository.objects.all(),
739
740
  widget=forms.MultipleHiddenInput(),
740
741
  )
741
- remote_url = forms.CharField(
742
+ remote_url = LaxURLField(
742
743
  label="Remote URL",
743
744
  required=False,
744
745
  )
@@ -1012,6 +1013,10 @@ class JobHookForm(BootstrapMixin, forms.ModelForm):
1012
1013
  content_types = MultipleContentTypeField(
1013
1014
  queryset=ChangeLoggedModelsQuery().as_queryset(), required=True, label="Content Type(s)"
1014
1015
  )
1016
+ job = DynamicModelChoiceField(
1017
+ queryset=Job.objects.filter(is_job_hook_receiver=True),
1018
+ query_params={"is_job_hook_receiver": True},
1019
+ )
1015
1020
 
1016
1021
  class Meta:
1017
1022
  model = JobHook
@@ -1179,14 +1184,19 @@ class JobButtonForm(BootstrapMixin, forms.ModelForm):
1179
1184
  api_url="/api/extras/content-types/",
1180
1185
  ),
1181
1186
  )
1187
+ job = DynamicModelChoiceField(
1188
+ queryset=Job.objects.filter(is_job_button_receiver=True),
1189
+ query_params={"is_job_button_receiver": True},
1190
+ )
1182
1191
 
1183
1192
  class Meta:
1184
1193
  model = JobButton
1185
1194
  fields = (
1186
1195
  "content_types",
1187
1196
  "name",
1188
- "text",
1189
1197
  "job",
1198
+ "enabled",
1199
+ "text",
1190
1200
  "weight",
1191
1201
  "group_name",
1192
1202
  "button_class",
@@ -1206,6 +1216,9 @@ class JobButtonBulkEditForm(BootstrapMixin, BulkEditForm):
1206
1216
  ),
1207
1217
  required=False,
1208
1218
  )
1219
+ enabled = forms.NullBooleanField(
1220
+ required=False, widget=BulkEditNullBooleanSelect, help_text="Whether this job button appears in the UI"
1221
+ )
1209
1222
  weight = forms.IntegerField(required=False)
1210
1223
  group_name = forms.CharField(required=False)
1211
1224
 
nautobot/extras/jobs.py CHANGED
@@ -1168,6 +1168,14 @@ def enqueue_job_hooks(object_change):
1168
1168
  job_hooks = JobHook.objects.filter(content_types=content_type, enabled=True, **{action_flag: True})
1169
1169
 
1170
1170
  # Enqueue the jobs related to the job_hooks
1171
+ get_jobs(reload=True)
1171
1172
  for job_hook in job_hooks:
1172
1173
  job_model = job_hook.job
1173
- JobResult.enqueue_job(job_model, object_change.user, object_change=object_change.pk)
1174
+ if not job_model.installed or not job_model.enabled:
1175
+ logger.warning(
1176
+ "JobHook %s is enabled, but the underlying Job %s is not installed and enabled", job_hook, job_model
1177
+ )
1178
+ elif get_job(job_model.class_path) is None:
1179
+ logger.error("JobHook %s is enabled, but the underlying Job implementation is missing", job_hook)
1180
+ else:
1181
+ JobResult.enqueue_job(job_model, object_change.user, object_change=object_change.pk)
@@ -0,0 +1,28 @@
1
+ # Generated by Django 3.2.25 on 2024-06-17 13:24
2
+
3
+ from django.db import migrations
4
+
5
+ import nautobot.core.models.fields
6
+ import nautobot.core.models.validators
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+ dependencies = [
11
+ ("extras", "0106_populate_default_statuses_and_roles_for_contact_associations"),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AlterField(
16
+ model_name="externalintegration",
17
+ name="remote_url",
18
+ field=nautobot.core.models.fields.LaxURLField(max_length=500),
19
+ ),
20
+ migrations.AlterField(
21
+ model_name="gitrepository",
22
+ name="remote_url",
23
+ field=nautobot.core.models.fields.LaxURLField(
24
+ max_length=255,
25
+ validators=[nautobot.core.models.validators.EnhancedURLValidator(schemes=["http", "https"])],
26
+ ),
27
+ ),
28
+ ]
@@ -0,0 +1,17 @@
1
+ # Generated by Django 3.2.25 on 2024-06-17 19:06
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("extras", "0107_laxurlfield"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AddField(
13
+ model_name="jobbutton",
14
+ name="enabled",
15
+ field=models.BooleanField(default=True),
16
+ ),
17
+ ]
@@ -6,18 +6,19 @@ import os
6
6
  from django.conf import settings
7
7
  from django.core.exceptions import ValidationError
8
8
  from django.core.serializers.json import DjangoJSONEncoder
9
- from django.core.validators import URLValidator
10
9
  from django.db import models
11
10
 
12
11
  from nautobot.core.constants import CHARFIELD_MAX_LENGTH
13
- from nautobot.core.models.fields import AutoSlugField, slugify_dashes_to_underscores
12
+ from nautobot.core.models.fields import AutoSlugField, LaxURLField, slugify_dashes_to_underscores
14
13
  from nautobot.core.models.generics import PrimaryModel
14
+ from nautobot.core.models.validators import EnhancedURLValidator
15
15
  from nautobot.extras.utils import check_if_key_is_graphql_safe, extras_features
16
16
 
17
17
 
18
18
  @extras_features(
19
19
  "config_context_owners",
20
20
  "export_template_owners",
21
+ "graphql",
21
22
  "job_results",
22
23
  "webhooks",
23
24
  )
@@ -34,15 +35,16 @@ class GitRepository(PrimaryModel):
34
35
  slugify_function=slugify_dashes_to_underscores,
35
36
  )
36
37
 
37
- remote_url = models.URLField(
38
+ remote_url = LaxURLField(
38
39
  max_length=CHARFIELD_MAX_LENGTH,
39
40
  # For the moment we don't support ssh:// and git:// URLs
40
41
  help_text="Only HTTP and HTTPS URLs are presently supported",
41
- validators=[URLValidator(schemes=["http", "https"])],
42
+ validators=[EnhancedURLValidator(schemes=["http", "https"])],
42
43
  )
43
44
  branch = models.CharField(
44
45
  max_length=CHARFIELD_MAX_LENGTH,
45
46
  default="main",
47
+ help_text="Branch, tag, or commit",
46
48
  )
47
49
 
48
50
  current_head = models.CharField(
@@ -597,10 +597,17 @@ class DynamicGroup(OrganizationalModel):
597
597
  query |= filter_field.generate_query(gq_value)
598
598
 
599
599
  # For vanilla multiple-choice filters, we want all values in a set union (boolean OR)
600
- # because we want ANY of the filter values to match.
600
+ # because we want ANY of the filter values to match. Unless the filter field is explicitly
601
+ # conjoining the values, in which case we want a set intersection (boolean AND). We know this isn't right
602
+ # since the resulting query actually does tag.name == tag_1 AND tag.name == tag_2, but django_filter does
603
+ # not use Q evaluation for conjoined filters. This function is only used for the display, and the display
604
+ # is good enough to get the point across.
601
605
  elif isinstance(filter_field, django_filters.MultipleChoiceFilter):
602
606
  for v in value:
603
- query |= models.Q(**filter_field.get_filter_predicate(v))
607
+ if filter_field.conjoined:
608
+ query &= models.Q(**filter_field.get_filter_predicate(v))
609
+ else:
610
+ query |= models.Q(**filter_field.get_filter_predicate(v))
604
611
 
605
612
  # The method `get_filter_predicate()` is only available on instances or subclasses
606
613
  # of `MultipleChoiceFilter`, so we must construct a lookup if a filter is not
@@ -318,6 +318,25 @@ class Job(PrimaryModel):
318
318
  {"approval_required": "A job that may have sensitive variables cannot be marked as requiring approval"}
319
319
  )
320
320
 
321
+ def save(self, *args, **kwargs):
322
+ """When a Job is uninstalled, auto-disable all associated JobButtons, JobHooks, and ScheduledJobs."""
323
+ super().save(*args, **kwargs)
324
+ if not self.installed:
325
+ if self.is_job_button_receiver:
326
+ for jb in JobButton.objects.filter(job=self, enabled=True):
327
+ logger.info("Disabling JobButton %s derived from %s", jb, self)
328
+ jb.enabled = False
329
+ jb.save()
330
+ if self.is_job_hook_receiver:
331
+ for jh in JobHook.objects.filter(job=self, enabled=True):
332
+ logger.info("Disabling JobHook %s derived from %s", jh, self)
333
+ jh.enabled = False
334
+ jh.save()
335
+ for sj in ScheduledJob.objects.filter(job_model=self, enabled=True):
336
+ logger.info("Disabling ScheduledJob %s derived from %s", sj, self)
337
+ sj.enabled = False
338
+ sj.save()
339
+
321
340
 
322
341
  @extras_features("graphql")
323
342
  class JobHook(OrganizationalModel):
@@ -363,6 +382,9 @@ class JobHook(OrganizationalModel):
363
382
  if not self.type_create and not self.type_delete and not self.type_update:
364
383
  raise ValidationError("You must select at least one type: create, update, and/or delete.")
365
384
 
385
+ if self.enabled and not (self.job.installed and self.job.enabled):
386
+ raise ValidationError({"enabled": "The selected Job is not installed and enabled"})
387
+
366
388
  @classmethod
367
389
  def check_for_conflicts(
368
390
  cls, instance=None, content_types=None, job=None, type_create=None, type_update=None, type_delete=None
@@ -375,6 +397,7 @@ class JobHook(OrganizationalModel):
375
397
  """
376
398
 
377
399
  conflicts = {}
400
+
378
401
  job_hook_error_msg = "A job hook already exists for {action} on {content_type} to job {job}"
379
402
 
380
403
  if instance is not None and instance.present_in_database:
@@ -783,6 +806,7 @@ class JobButton(BaseModel, ChangeLoggedModel, NotesMixin):
783
806
  help_text="The object type(s) to which this job button applies.",
784
807
  )
785
808
  name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, unique=True)
809
+ enabled = models.BooleanField(default=True)
786
810
  text = models.CharField(
787
811
  max_length=500,
788
812
  help_text="Jinja2 template code for button text. Reference the object as <code>{{ obj }}</code> such as <code>{{ obj.platform.name }}</code>. Buttons which render as empty text will not be displayed.",
@@ -817,6 +841,12 @@ class JobButton(BaseModel, ChangeLoggedModel, NotesMixin):
817
841
  def __str__(self):
818
842
  return self.name
819
843
 
844
+ def clean(self):
845
+ super().clean()
846
+
847
+ if self.enabled and not (self.job.installed and self.job.enabled):
848
+ raise ValidationError({"enabled": "The selected Job is not installed and enabled"})
849
+
820
850
 
821
851
  class ScheduledJobs(models.Model):
822
852
  """Helper table for tracking updates to scheduled tasks.
@@ -22,9 +22,8 @@ from rest_framework.utils.encoders import JSONEncoder
22
22
 
23
23
  from nautobot.core.constants import CHARFIELD_MAX_LENGTH
24
24
  from nautobot.core.models import BaseManager, BaseModel
25
- from nautobot.core.models.fields import ForeignKeyWithAutoRelatedName
25
+ from nautobot.core.models.fields import ForeignKeyWithAutoRelatedName, LaxURLField
26
26
  from nautobot.core.models.generics import OrganizationalModel, PrimaryModel
27
- from nautobot.core.models.validators import EnhancedURLValidator
28
27
  from nautobot.core.utils.data import deepmerge, render_jinja2
29
28
  from nautobot.extras.choices import (
30
29
  ButtonClassChoices,
@@ -442,10 +441,9 @@ class ExternalIntegration(PrimaryModel):
442
441
  """Model for tracking integrations with external applications."""
443
442
 
444
443
  name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, unique=True)
445
- remote_url = models.CharField(
444
+ remote_url = LaxURLField(
446
445
  max_length=500,
447
446
  verbose_name="Remote URL",
448
- validators=[EnhancedURLValidator()],
449
447
  )
450
448
  secrets_group = models.ForeignKey(
451
449
  null=True,
nautobot/extras/tables.py CHANGED
@@ -818,6 +818,7 @@ class JobButtonTable(BaseTable):
818
818
  pk = ToggleColumn()
819
819
  name = tables.Column(linkify=True)
820
820
  job = tables.Column(linkify=True)
821
+ enabled = BooleanColumn()
821
822
  confirmation = BooleanColumn()
822
823
  content_types = ContentTypesColumn(truncate_words=15)
823
824
 
@@ -829,6 +830,7 @@ class JobButtonTable(BaseTable):
829
830
  "content_types",
830
831
  "text",
831
832
  "job",
833
+ "enabled",
832
834
  "group_name",
833
835
  "weight",
834
836
  "button_class",
@@ -841,6 +843,7 @@ class JobButtonTable(BaseTable):
841
843
  "group_name",
842
844
  "weight",
843
845
  "job",
846
+ "enabled",
844
847
  "confirmation",
845
848
  )
846
849
 
@@ -32,19 +32,23 @@
32
32
  </tr>
33
33
  <tr>
34
34
  <td>Text</td>
35
- <td><span>{{ object.text }}</span></td>
35
+ <td><pre>{{ object.text }}</pre></td>
36
36
  </tr>
37
37
  <tr>
38
38
  <td>Job</td>
39
39
  <td><a href="{% url 'extras:job_run_by_class_path' class_path=object.job.class_path %}">{{ object.job }}</a></td>
40
40
  </tr>
41
+ <tr>
42
+ <td>Enabled</td>
43
+ <td>{{ object.enabled|render_boolean }}</td>
44
+ </tr>
41
45
  <tr>
42
46
  <td>Button Class</td>
43
47
  <td><button class="btn btn-{{ object.button_class }}">{{ object.button_class | title }}</button></td>
44
48
  </tr>
45
49
  <tr>
46
50
  <td>Confirmation</td>
47
- <td><span>{{ object.confirmation }}</span></td>
51
+ <td>{{ object.confirmation|render_boolean}}</td>
48
52
  </tr>
49
53
  </table>
50
54
  </div>
@@ -127,7 +127,7 @@ def _render_job_button_for_obj(job_button, obj, context, content_type):
127
127
  "object": obj,
128
128
  "job": job_button.job,
129
129
  "hidden_inputs": hidden_inputs,
130
- "disabled": "" if has_run_perm else "disabled",
130
+ "disabled": "" if (has_run_perm and job_button.job.installed and job_button.job.enabled) else "disabled",
131
131
  }
132
132
 
133
133
  if job_button.confirmation:
@@ -149,7 +149,7 @@ def job_buttons(context, obj):
149
149
  """
150
150
  content_type = ContentType.objects.get_for_model(obj)
151
151
  # We will enforce "run" permission later in deciding which buttons to show as disabled.
152
- buttons = JobButton.objects.filter(content_types=content_type)
152
+ buttons = JobButton.objects.filter(content_types=content_type, enabled=True)
153
153
  if not buttons:
154
154
  return SAFE_EMPTY_STR
155
155
 
@@ -0,0 +1,18 @@
1
+ ---
2
+ _metadata:
3
+ name: "Config Context Schema 1"
4
+ description: "Schema for defining first names, last names, and ages."
5
+ data_schema:
6
+ title: "Person"
7
+ type: "object"
8
+ properties:
9
+ firstName:
10
+ type: "string"
11
+ description: "The person's first name."
12
+ lastName:
13
+ type: "string"
14
+ description: "The person's last name."
15
+ age:
16
+ description: "Age in years which must be equal to or greater than zero."
17
+ type: "integer"
18
+ minimum: 0
@@ -0,0 +1,12 @@
1
+ ---
2
+ _metadata:
3
+ name: "Frobozz 1000 NTP servers"
4
+ weight: 1500
5
+ description: "NTP servers for Frobozz 1000 devices **only**"
6
+ is_active: true
7
+ config_context_schema: "Config Context Schema 1"
8
+ device_types:
9
+ - model: "Frobozz 1000"
10
+ ntp-servers:
11
+ - "172.16.10.22"
12
+ - "172.16.10.33"
@@ -0,0 +1,3 @@
1
+ {
2
+ "dns-servers": ["8.8.8.8"]
3
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "_metadata": {
3
+ "name": "Location context",
4
+ "is_active": false
5
+ },
6
+ "domain_name": "example.com"
7
+ }
@@ -0,0 +1,3 @@
1
+ {% for device in queryset %}
2
+ {{ device.name }}
3
+ {% endfor %}
@@ -0,0 +1,4 @@
1
+ <!DOCTYPE html>
2
+ {% for device in queryset %}
3
+ {{ device.name }}
4
+ {% endfor %}
@@ -0,0 +1,3 @@
1
+ {% for vlan in queryset %}
2
+ {{ vlan.name }}
3
+ {% endfor %}
@@ -0,0 +1,5 @@
1
+ from nautobot.core.celery import register_jobs
2
+
3
+ from .my_job import MyJob, MyJobButtonReceiver, MyJobHookReceiver
4
+
5
+ register_jobs(MyJob, MyJobButtonReceiver, MyJobHookReceiver)
@@ -0,0 +1,16 @@
1
+ from nautobot.extras.jobs import Job, JobButtonReceiver, JobHookReceiver
2
+
3
+
4
+ class MyJob(Job):
5
+ def run(self):
6
+ pass
7
+
8
+
9
+ class MyJobHookReceiver(JobHookReceiver):
10
+ def receive_job_hook(self, change, action, changed_object):
11
+ pass
12
+
13
+
14
+ class MyJobButtonReceiver(JobButtonReceiver):
15
+ def receive_job_button(self, obj):
16
+ pass
@@ -0,0 +1,3 @@
1
+ {% for nosuchmodel in queryset %}
2
+ {{ nosuchmodel.name }}
3
+ {% endfor %}
@@ -0,0 +1,3 @@
1
+ {% for device in queryset %}
2
+ {{ device.name }}
3
+ {% endfor %}
@@ -0,0 +1,2 @@
1
+ import .syntaxerror
2
+ import .importerror
@@ -0,0 +1 @@
1
+ import nosuchmodule
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python
2
+
3
+ import logging
4
+ import os
5
+ import os.path
6
+ import shutil
7
+ import tempfile
8
+
9
+ from git import Repo
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ SOURCE_DIR = os.path.join(os.path.dirname(__file__), "git_data")
15
+
16
+
17
+ def create_and_populate_git_repository(target_path):
18
+ """
19
+ Create a Git repository in `target_path` and populate it with commits and tags based on contents of `SOURCE_DIR`.
20
+
21
+ An initial commit will always be created, containing no tracked files, and will be given the tag `empty-repo`.
22
+ After that, each subdir under `SOURCE_DIR` will be used as the basis for a single commit to the repo, so given:
23
+
24
+ nautobot/extras/tests/git_data
25
+ ├── 01-valid-files
26
+ │ ├── config_context_schemas
27
+ │ │ └── schema-1.yaml
28
+ │ └── config_contexts
29
+ │ └── context.yaml
30
+ └── 02-invalid-files
31
+ └── config_context_schemas
32
+ ├── badschema1.json
33
+ └── badschema2.json
34
+
35
+ ...after the initial empty commit, the next commit would contain files `config_context_schemas/schema-1.yaml` and
36
+ `config_contexts/context.yaml`, and would be given the tag `valid-files`. The next commit would remove those files
37
+ but add the files `config_context_schemas/badschema1.json` and `config_context_schemas/badschema2.json`, and would
38
+ be tagged as `invalid-files`.
39
+
40
+ Note that each commit is fully defined by the files in the appropriate subdirectory; if you want a file to exist
41
+ across multiple separate commits, it must exist in multiple subdirectories. Use of symlinks is encouraged in such
42
+ a scenario.
43
+ """
44
+ os.makedirs(target_path, exist_ok=True)
45
+ repo = Repo.init(target_path, initial_branch="main")
46
+ repo.config_writer().set_value("user", "name", "Nautobot Automation").release()
47
+ repo.config_writer().set_value("user", "email", "nautobot@nautobot.com").release()
48
+
49
+ repo.index.commit("Empty commit")
50
+ repo.create_tag("empty-repo", message="Nothing here yet")
51
+
52
+ # Create a commit matching each subdirectory in the git_data directory
53
+ for dirname in sorted(os.listdir(SOURCE_DIR)):
54
+ # Clean up from any previous commit
55
+ for root, _, files in os.walk(target_path):
56
+ if ".git" in root:
57
+ continue
58
+ for filename in files:
59
+ repo.index.remove([os.path.join(root, filename)])
60
+ os.remove(os.path.join(root, filename))
61
+
62
+ shutil.copytree(os.path.join(SOURCE_DIR, dirname), target_path, dirs_exist_ok=True)
63
+ for root, _, files in os.walk(target_path):
64
+ if ".git" in root:
65
+ continue
66
+ for filename in files:
67
+ repo.index.add([os.path.join(root, filename)])
68
+ repo.index.commit(dirname)
69
+ # Directory "01-valid-files" --> tag "valid-files" so that we won't break tests if we renumber the directories
70
+ repo.create_tag(dirname[3:], message=f"Tag based on {dirname} files")
71
+
72
+
73
+ if __name__ == "__main__":
74
+ directory_path = tempfile.TemporaryDirectory().name # pylint: disable=consider-using-with
75
+ print(f"Creating test Git repository in {directory_path}...")
76
+ create_and_populate_git_repository(directory_path)