nautobot 2.2.2__py3-none-any.whl → 2.2.4__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 (362) hide show
  1. nautobot/apps/jobs.py +2 -0
  2. nautobot/core/api/utils.py +12 -9
  3. nautobot/core/apps/__init__.py +2 -2
  4. nautobot/core/celery/__init__.py +79 -68
  5. nautobot/core/celery/backends.py +9 -1
  6. nautobot/core/celery/control.py +4 -7
  7. nautobot/core/celery/schedulers.py +4 -2
  8. nautobot/core/celery/task.py +78 -5
  9. nautobot/core/graphql/schema.py +2 -1
  10. nautobot/core/jobs/__init__.py +2 -1
  11. nautobot/core/settings.py +6 -4
  12. nautobot/core/settings.yaml +51 -16
  13. nautobot/core/templates/admin/base.html +2 -2
  14. nautobot/core/templates/base_django.html +2 -2
  15. nautobot/core/templates/buttons/export.html +47 -47
  16. nautobot/core/templates/generic/object_list.html +3 -3
  17. nautobot/core/templates/inc/javascript.html +3 -0
  18. nautobot/core/templates/inc/media.html +3 -0
  19. nautobot/core/templates/login.html +2 -2
  20. nautobot/core/templates/nautobot_config.py.j2 +2 -0
  21. nautobot/core/templatetags/helpers.py +66 -9
  22. nautobot/core/testing/__init__.py +6 -1
  23. nautobot/core/testing/api.py +12 -13
  24. nautobot/core/testing/mixins.py +2 -2
  25. nautobot/core/testing/views.py +50 -51
  26. nautobot/core/tests/test_api.py +23 -2
  27. nautobot/core/tests/test_jobs.py +79 -2
  28. nautobot/core/tests/test_templatetags_helpers.py +32 -0
  29. nautobot/core/tests/test_views.py +52 -0
  30. nautobot/core/tests/test_views_utils.py +22 -1
  31. nautobot/core/utils/module_loading.py +89 -0
  32. nautobot/core/views/mixins.py +4 -0
  33. nautobot/core/views/utils.py +3 -2
  34. nautobot/dcim/choices.py +14 -0
  35. nautobot/dcim/forms.py +51 -1
  36. nautobot/dcim/models/device_components.py +9 -5
  37. nautobot/dcim/templates/dcim/location.html +32 -13
  38. nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +102 -0
  39. nautobot/dcim/tests/test_views.py +376 -55
  40. nautobot/dcim/urls.py +5 -0
  41. nautobot/dcim/views.py +172 -21
  42. nautobot/extras/api/serializers.py +17 -6
  43. nautobot/extras/api/views.py +21 -10
  44. nautobot/extras/constants.py +3 -3
  45. nautobot/extras/datasources/git.py +47 -58
  46. nautobot/extras/forms/forms.py +3 -1
  47. nautobot/extras/jobs.py +79 -146
  48. nautobot/extras/models/datasources.py +0 -2
  49. nautobot/extras/models/jobs.py +36 -18
  50. nautobot/extras/plugins/__init__.py +1 -20
  51. nautobot/extras/signals.py +6 -9
  52. nautobot/extras/test_jobs/__init__.py +8 -0
  53. nautobot/extras/test_jobs/dry_run.py +3 -2
  54. nautobot/extras/test_jobs/fail.py +43 -0
  55. nautobot/extras/test_jobs/ipaddress_vars.py +40 -1
  56. nautobot/extras/test_jobs/jobs_module/__init__.py +5 -0
  57. nautobot/extras/test_jobs/jobs_module/jobs_submodule/__init__.py +1 -0
  58. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +6 -0
  59. nautobot/extras/test_jobs/pass.py +40 -0
  60. nautobot/extras/test_jobs/relative_import.py +11 -0
  61. nautobot/extras/tests/test_api.py +3 -0
  62. nautobot/extras/tests/test_context_managers.py +18 -0
  63. nautobot/extras/tests/test_datasources.py +125 -118
  64. nautobot/extras/tests/test_job_variables.py +57 -15
  65. nautobot/extras/tests/test_jobs.py +135 -1
  66. nautobot/extras/tests/test_models.py +26 -19
  67. nautobot/extras/tests/test_plugins.py +1 -3
  68. nautobot/extras/tests/test_views.py +2 -4
  69. nautobot/extras/utils.py +2 -1
  70. nautobot/extras/views.py +82 -116
  71. nautobot/ipam/api/views.py +8 -1
  72. nautobot/ipam/graphql/types.py +11 -0
  73. nautobot/ipam/mixins.py +32 -0
  74. nautobot/ipam/models.py +2 -1
  75. nautobot/ipam/querysets.py +6 -1
  76. nautobot/ipam/tests/test_models.py +82 -0
  77. nautobot/ipam/views.py +6 -6
  78. nautobot/project-static/docs/404.html +107 -51
  79. nautobot/project-static/docs/apps/index.html +107 -51
  80. nautobot/project-static/docs/apps/nautobot-apps.html +107 -51
  81. nautobot/project-static/docs/assets/_mkdocstrings.css +6 -1
  82. nautobot/project-static/docs/assets/extra.css +11 -0
  83. nautobot/project-static/docs/assets/javascripts/bundle.3220b9d7.min.js +29 -0
  84. nautobot/project-static/docs/assets/javascripts/bundle.3220b9d7.min.js.map +7 -0
  85. nautobot/project-static/docs/assets/stylesheets/main.66ac8b77.min.css +1 -0
  86. nautobot/project-static/docs/assets/stylesheets/main.66ac8b77.min.css.map +1 -0
  87. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +107 -51
  88. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +107 -51
  89. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +108 -52
  90. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +107 -51
  91. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +107 -51
  92. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +107 -51
  93. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +107 -51
  94. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +107 -51
  95. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +107 -51
  96. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +107 -51
  97. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +107 -51
  98. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +107 -51
  99. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +107 -51
  100. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +287 -262
  101. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +107 -51
  102. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +107 -51
  103. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +107 -51
  104. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +107 -51
  105. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -51
  106. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +107 -51
  107. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +107 -51
  108. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +107 -51
  109. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +107 -51
  110. nautobot/project-static/docs/development/apps/api/configuration-view.html +110 -54
  111. nautobot/project-static/docs/development/apps/api/database-backend-config.html +110 -54
  112. nautobot/project-static/docs/development/apps/api/models/django-admin.html +107 -51
  113. nautobot/project-static/docs/development/apps/api/models/global-search.html +110 -54
  114. nautobot/project-static/docs/development/apps/api/models/graphql.html +113 -57
  115. nautobot/project-static/docs/development/apps/api/models/index.html +107 -51
  116. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +113 -57
  117. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +107 -51
  118. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +110 -54
  119. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +107 -51
  120. nautobot/project-static/docs/development/apps/api/platform-features/index.html +107 -51
  121. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +110 -54
  122. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +111 -55
  123. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +110 -54
  124. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +110 -54
  125. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +107 -51
  126. nautobot/project-static/docs/development/apps/api/prometheus.html +110 -54
  127. nautobot/project-static/docs/development/apps/api/setup.html +107 -51
  128. nautobot/project-static/docs/development/apps/api/testing.html +113 -57
  129. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +110 -54
  130. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +110 -54
  131. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +107 -51
  132. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +107 -51
  133. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +113 -57
  134. nautobot/project-static/docs/development/apps/api/views/base-template.html +107 -51
  135. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +110 -54
  136. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +107 -51
  137. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +110 -54
  138. nautobot/project-static/docs/development/apps/api/views/index.html +107 -51
  139. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +113 -57
  140. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +122 -66
  141. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +110 -54
  142. nautobot/project-static/docs/development/apps/api/views/notes.html +110 -54
  143. nautobot/project-static/docs/development/apps/api/views/rest-api.html +107 -51
  144. nautobot/project-static/docs/development/apps/api/views/urls.html +107 -51
  145. nautobot/project-static/docs/development/apps/index.html +128 -72
  146. nautobot/project-static/docs/development/apps/migration/code-updates.html +107 -51
  147. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +107 -51
  148. nautobot/project-static/docs/development/apps/migration/from-v1.html +107 -51
  149. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +107 -51
  150. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +107 -51
  151. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +107 -51
  152. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +107 -51
  153. nautobot/project-static/docs/development/apps/porting-from-netbox.html +110 -54
  154. nautobot/project-static/docs/development/core/application-registry.html +242 -144
  155. nautobot/project-static/docs/development/core/best-practices.html +122 -66
  156. nautobot/project-static/docs/development/core/bootstrap-ui.html +107 -51
  157. nautobot/project-static/docs/development/core/caching.html +107 -51
  158. nautobot/project-static/docs/development/core/controllers.html +107 -51
  159. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +113 -57
  160. nautobot/project-static/docs/development/core/generic-views.html +110 -54
  161. nautobot/project-static/docs/development/core/getting-started.html +135 -79
  162. nautobot/project-static/docs/development/core/homepage.html +110 -54
  163. nautobot/project-static/docs/development/core/index.html +107 -51
  164. nautobot/project-static/docs/development/core/model-checklist.html +156 -52
  165. nautobot/project-static/docs/development/core/model-features.html +108 -52
  166. nautobot/project-static/docs/development/core/natural-keys.html +110 -54
  167. nautobot/project-static/docs/development/core/navigation-menu.html +107 -51
  168. nautobot/project-static/docs/development/core/release-checklist.html +107 -51
  169. nautobot/project-static/docs/development/core/role-internals.html +107 -51
  170. nautobot/project-static/docs/development/core/settings.html +107 -51
  171. nautobot/project-static/docs/development/core/style-guide.html +110 -54
  172. nautobot/project-static/docs/development/core/templates.html +113 -57
  173. nautobot/project-static/docs/development/core/testing.html +125 -69
  174. nautobot/project-static/docs/development/core/user-preferences.html +107 -51
  175. nautobot/project-static/docs/development/index.html +107 -51
  176. nautobot/project-static/docs/development/jobs/index.html +504 -172
  177. nautobot/project-static/docs/development/jobs/migration/from-v1.html +111 -55
  178. nautobot/project-static/docs/docker/index.html +3 -3
  179. nautobot/project-static/docs/index.html +125 -69
  180. nautobot/project-static/docs/installation/selinux-troubleshooting.html +3 -3
  181. nautobot/project-static/docs/objects.inv +0 -0
  182. nautobot/project-static/docs/release-notes/index.html +107 -51
  183. nautobot/project-static/docs/release-notes/version-1.0.html +107 -51
  184. nautobot/project-static/docs/release-notes/version-1.1.html +107 -51
  185. nautobot/project-static/docs/release-notes/version-1.2.html +107 -51
  186. nautobot/project-static/docs/release-notes/version-1.3.html +107 -51
  187. nautobot/project-static/docs/release-notes/version-1.4.html +107 -51
  188. nautobot/project-static/docs/release-notes/version-1.5.html +116 -60
  189. nautobot/project-static/docs/release-notes/version-1.6.html +107 -51
  190. nautobot/project-static/docs/release-notes/version-2.0.html +110 -54
  191. nautobot/project-static/docs/release-notes/version-2.1.html +107 -51
  192. nautobot/project-static/docs/release-notes/version-2.2.html +500 -114
  193. nautobot/project-static/docs/requirements.txt +2 -2
  194. nautobot/project-static/docs/search/search_index.json +1 -1
  195. nautobot/project-static/docs/sitemap.xml +262 -262
  196. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  197. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +107 -51
  198. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +107 -51
  199. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +107 -51
  200. nautobot/project-static/docs/user-guide/administration/configuration/index.html +107 -51
  201. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +251 -164
  202. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +113 -57
  203. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +107 -51
  204. nautobot/project-static/docs/user-guide/administration/guides/caching.html +113 -57
  205. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +107 -51
  206. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +107 -51
  207. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +107 -51
  208. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +113 -57
  209. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +107 -51
  210. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +107 -51
  211. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +107 -51
  212. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +171 -112
  213. nautobot/project-static/docs/user-guide/administration/installation/docker.html +13 -8626
  214. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +117 -61
  215. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +13 -8614
  216. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +252 -165
  217. nautobot/project-static/docs/user-guide/administration/installation/index.html +165 -192
  218. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +411 -691
  219. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +248 -229
  220. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +13 -8118
  221. nautobot/project-static/docs/user-guide/administration/installation/services.html +350 -240
  222. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +8684 -0
  223. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +8672 -0
  224. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +8176 -0
  225. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +110 -54
  226. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +110 -54
  227. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +155 -99
  228. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +107 -51
  229. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +109 -53
  230. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +107 -51
  231. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +107 -51
  232. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +107 -51
  233. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +107 -51
  234. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +107 -51
  235. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +107 -51
  236. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +117 -58
  237. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +113 -57
  238. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +107 -51
  239. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +107 -51
  240. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +107 -51
  241. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +107 -51
  242. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +110 -54
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +107 -51
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +110 -54
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +110 -54
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +110 -54
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +110 -54
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +107 -51
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +107 -51
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +113 -57
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +110 -54
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +110 -54
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +110 -54
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +110 -54
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +116 -60
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +110 -54
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +110 -54
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +119 -63
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +110 -54
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +110 -54
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +113 -57
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +113 -57
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +113 -57
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +107 -51
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +113 -57
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +107 -51
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +110 -54
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +110 -54
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +107 -51
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +110 -54
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +110 -54
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +107 -51
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +107 -51
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +107 -51
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +110 -54
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +110 -54
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +110 -54
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +110 -54
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +107 -51
  280. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +113 -57
  281. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +110 -54
  282. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +110 -54
  283. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +110 -54
  284. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +125 -69
  285. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +113 -57
  286. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +128 -72
  287. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +110 -54
  288. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +107 -51
  289. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +110 -54
  290. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +227 -60
  291. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +107 -51
  292. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +113 -57
  293. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +107 -51
  294. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +110 -54
  295. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +107 -51
  296. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +107 -51
  297. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +107 -51
  298. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +107 -51
  299. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +113 -57
  300. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +113 -57
  301. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +107 -51
  302. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +113 -57
  303. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +107 -51
  304. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +107 -51
  305. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +107 -51
  306. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +107 -51
  307. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +107 -51
  308. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +107 -51
  309. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +107 -51
  310. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +107 -51
  311. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +107 -51
  312. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +110 -54
  313. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +113 -57
  314. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +107 -51
  315. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +110 -54
  316. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +107 -51
  317. nautobot/project-static/docs/user-guide/index.html +109 -53
  318. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +107 -51
  319. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +113 -57
  320. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +128 -72
  321. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +107 -51
  322. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +125 -69
  323. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +107 -51
  324. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +110 -54
  325. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +125 -69
  326. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +110 -54
  327. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +107 -51
  328. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +107 -51
  329. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +143 -100
  330. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +113 -57
  331. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +113 -57
  332. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +110 -54
  333. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +123 -67
  334. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +107 -51
  335. nautobot/project-static/docs/user-guide/platform-functionality/note.html +110 -54
  336. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +116 -60
  337. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +110 -54
  338. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +131 -75
  339. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +149 -93
  340. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +110 -54
  341. nautobot/project-static/docs/user-guide/platform-functionality/role.html +107 -51
  342. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +116 -60
  343. nautobot/project-static/docs/user-guide/platform-functionality/status.html +119 -63
  344. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +110 -54
  345. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +137 -81
  346. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +110 -54
  347. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +107 -51
  348. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +110 -54
  349. nautobot/project-static/js/forms.js +18 -11
  350. nautobot/tenancy/views.py +2 -6
  351. nautobot/virtualization/views.py +5 -9
  352. {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/METADATA +4 -4
  353. {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/RECORD +357 -348
  354. nautobot/extras/test_jobs/job_variables.py +0 -93
  355. nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js +0 -29
  356. nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js.map +0 -7
  357. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +0 -1
  358. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +0 -1
  359. {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/LICENSE.txt +0 -0
  360. {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/NOTICE +0 -0
  361. {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/WHEEL +0 -0
  362. {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,10 @@
1
+ import urllib.parse
2
+
1
3
  from django.db import ProgrammingError
2
4
  from django.test import TestCase
3
5
 
4
6
  from nautobot.core.models.querysets import count_related
5
- from nautobot.core.views.utils import check_filter_for_display
7
+ from nautobot.core.views.utils import check_filter_for_display, prepare_cloned_fields
6
8
  from nautobot.dcim.filters import DeviceFilterSet
7
9
  from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, InventoryItem, Location, Manufacturer
8
10
  from nautobot.extras.models import Role, Status
@@ -147,3 +149,22 @@ class CheckCountRelatedSubquery(TestCase):
147
149
  manufacturer_count=count_related(Manufacturer, "inventory_items__device", distinct=True)
148
150
  )
149
151
  self.assertEqual(qs.get(pk=device1.pk).manufacturer_count, 3)
152
+
153
+
154
+ class CheckPrepareClonedFields(TestCase):
155
+ name = "Building-02"
156
+ descriptions = ["Complicated Name & Stuff", "Simple Name"]
157
+
158
+ def testQueryParameterGeneration(self):
159
+ """Assert that a clone field with a special character, &, is properly escaped"""
160
+ instance = Location.objects.get(name=self.name)
161
+ self.assertIsInstance(instance, Location)
162
+ for description in self.descriptions:
163
+ with self.subTest(f"Testing parameter generation for a model with the name '{description}'"):
164
+ instance.description = description
165
+ query_params = urllib.parse.parse_qs(prepare_cloned_fields(instance))
166
+ self.assertTrue(isinstance(query_params, dict))
167
+ self.assertTrue("description" in query_params.keys())
168
+ self.assertTrue(isinstance(query_params["description"], list))
169
+ self.assertTrue(len(query_params["description"]) == 1)
170
+ self.assertTrue(query_params["description"][0] == description)
@@ -0,0 +1,89 @@
1
+ from contextlib import contextmanager
2
+ import importlib
3
+ from importlib.util import find_spec, module_from_spec
4
+ import logging
5
+ import os
6
+ import pkgutil
7
+ import sys
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ @contextmanager
13
+ def _temporarily_add_to_sys_path(path):
14
+ """
15
+ Allow loading of modules and packages from within the provided directory by temporarily modifying `sys.path`.
16
+
17
+ On exit, it restores the original `sys.path` value.
18
+ """
19
+ old_sys_path = sys.path.copy()
20
+ sys.path.insert(0, path)
21
+ try:
22
+ yield
23
+ finally:
24
+ sys.path = old_sys_path
25
+
26
+
27
+ def import_modules_privately(path, module_path=None, ignore_import_errors=True):
28
+ """
29
+ Import modules from the filesystem without adding the path permanently to `sys.path`.
30
+
31
+ This is used for importing Jobs from `JOBS_ROOT` and `GIT_ROOT` in such a way that they remain relatively
32
+ self-contained and can be easily discarded and reloaded on the fly.
33
+
34
+ If you find yourself writing new code that uses this method, please pause and reconsider your life choices.
35
+
36
+ Args:
37
+ path (str): Directory path possibly containing Python modules or packages to load.
38
+ module_path (list): If set to a non-empty list, only modules matching the given chain of modules will be loaded.
39
+ For example, `["my_git_repo", "jobs"]`.
40
+ ignore_import_errors (bool): Exceptions raised while importing modules will be caught and logged.
41
+ If this is set as False, they will then be re-raised to be handled by the caller of this function.
42
+ """
43
+ if module_path is None:
44
+ module_path = []
45
+ module_prefix = None
46
+ else:
47
+ module_prefix = ".".join(module_path)
48
+ with _temporarily_add_to_sys_path(path):
49
+ for finder, discovered_module_name, is_package in pkgutil.walk_packages([path], onerror=logger.error):
50
+ if module_prefix and not (
51
+ module_prefix.startswith(f"{discovered_module_name}.") # my_repo/__init__.py
52
+ or discovered_module_name == module_prefix # my_repo/jobs.py
53
+ or discovered_module_name.startswith(f"{module_prefix}.") # my_repo/jobs/foobar.py
54
+ ):
55
+ continue
56
+ try:
57
+ existing_module = find_spec(discovered_module_name)
58
+ except (ModuleNotFoundError, ValueError):
59
+ existing_module = None
60
+ if existing_module is not None:
61
+ existing_module_path = os.path.realpath(existing_module.origin)
62
+ if not existing_module_path.startswith(path):
63
+ logger.error(
64
+ "Unable to load module %s from %s as it conflicts with existing module %s",
65
+ discovered_module_name,
66
+ path,
67
+ existing_module_path,
68
+ )
69
+ continue
70
+
71
+ if discovered_module_name in sys.modules:
72
+ del sys.modules[discovered_module_name]
73
+
74
+ try:
75
+ if not is_package:
76
+ spec = finder.find_spec(discovered_module_name)
77
+ if spec is None:
78
+ raise ValueError("Unable to find module spec")
79
+ module = module_from_spec(spec)
80
+ sys.modules[discovered_module_name] = module
81
+ spec.loader.exec_module(module)
82
+ else:
83
+ module = importlib.import_module(discovered_module_name)
84
+
85
+ importlib.reload(module)
86
+ except Exception as exc:
87
+ logger.error("Unable to load module %s from %s: %s", discovered_module_name, path, exc)
88
+ if not ignore_import_errors:
89
+ raise
@@ -485,6 +485,10 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
485
485
  request: The current request
486
486
  instance: The object being viewed
487
487
  """
488
+ if instance is not None:
489
+ return {
490
+ "active_tab": request.GET.get("tab", "main"),
491
+ }
488
492
  return {}
489
493
 
490
494
  def get_template_name(self):
@@ -1,5 +1,6 @@
1
1
  import datetime
2
2
  from io import BytesIO
3
+ import urllib.parse
3
4
 
4
5
  from django.contrib import messages
5
6
  from django.core.exceptions import FieldError, ValidationError
@@ -269,7 +270,7 @@ def prepare_cloned_fields(instance):
269
270
  for tag in instance.tags.all():
270
271
  params.append(("tags", tag.pk))
271
272
 
272
- # Concatenate parameters into a URL query string
273
- param_string = "&".join([f"{k}={v}" for k, v in params])
273
+ # Encode the parameters into a URL query string
274
+ param_string = urllib.parse.urlencode(params)
274
275
 
275
276
  return param_string
nautobot/dcim/choices.py CHANGED
@@ -21,6 +21,20 @@ class LocationStatusChoices(ChoiceSet):
21
21
  )
22
22
 
23
23
 
24
+ class LocationDataToContactActionChoices(ChoiceSet):
25
+ ASSOCIATE_EXISTING_CONTACT = "associate existing contact"
26
+ ASSOCIATE_EXISTING_TEAM = "associate existing team"
27
+ CREATE_AND_ASSIGN_NEW_CONTACT = "create and assign new contact"
28
+ CREATE_AND_ASSIGN_NEW_TEAM = "create and assign new team"
29
+
30
+ CHOICES = (
31
+ (ASSOCIATE_EXISTING_CONTACT, "Associate to existing contact"),
32
+ (ASSOCIATE_EXISTING_TEAM, "Associate to existing team"),
33
+ (CREATE_AND_ASSIGN_NEW_CONTACT, "Create and assign new contact"),
34
+ (CREATE_AND_ASSIGN_NEW_TEAM, "Create and assign new team"),
35
+ )
36
+
37
+
24
38
  #
25
39
  # Racks
26
40
  #
nautobot/dcim/forms.py CHANGED
@@ -53,7 +53,7 @@ from nautobot.extras.forms import (
53
53
  StatusModelFilterFormMixin,
54
54
  TagsBulkEditFormMixin,
55
55
  )
56
- from nautobot.extras.models import ExternalIntegration, SecretsGroup, Status
56
+ from nautobot.extras.models import Contact, ContactAssociation, ExternalIntegration, Role, SecretsGroup, Status, Team
57
57
  from nautobot.ipam.constants import BGP_ASN_MAX, BGP_ASN_MIN
58
58
  from nautobot.ipam.models import IPAddress, IPAddressToInterface, VLAN, VLANLocationAssignment, VRF
59
59
  from nautobot.tenancy.forms import TenancyFilterForm, TenancyForm
@@ -69,6 +69,7 @@ from .choices import (
69
69
  InterfaceModeChoices,
70
70
  InterfaceRedundancyGroupProtocolChoices,
71
71
  InterfaceTypeChoices,
72
+ LocationDataToContactActionChoices,
72
73
  PortTypeChoices,
73
74
  PowerFeedPhaseChoices,
74
75
  PowerFeedSupplyChoices,
@@ -371,6 +372,55 @@ class LocationFilterForm(NautobotFilterForm, StatusModelFilterFormMixin, Tenancy
371
372
  tags = TagFilterField(model)
372
373
 
373
374
 
375
+ class LocationMigrateDataToContactForm(NautobotModelForm):
376
+ # Assign tab form fields
377
+ action = forms.ChoiceField(
378
+ choices=LocationDataToContactActionChoices,
379
+ required=True,
380
+ widget=StaticSelect2(),
381
+ )
382
+ location = DynamicModelChoiceField(queryset=Location.objects.all(), required=False, label="Source Location")
383
+ contact = DynamicModelChoiceField(
384
+ queryset=Contact.objects.all(),
385
+ required=False,
386
+ label="Available Contacts",
387
+ query_params={"similar_to_location_data": "$location"},
388
+ )
389
+ team = DynamicModelChoiceField(
390
+ queryset=Team.objects.all(),
391
+ required=False,
392
+ label="Available Teams",
393
+ query_params={"similar_to_location_data": "$location"},
394
+ )
395
+ role = DynamicModelChoiceField(
396
+ queryset=Role.objects.all(),
397
+ required=True,
398
+ query_params={"content_types": ContactAssociation._meta.label_lower},
399
+ )
400
+ status = DynamicModelChoiceField(
401
+ queryset=Status.objects.all(),
402
+ required=True,
403
+ query_params={"content_types": ContactAssociation._meta.label_lower},
404
+ )
405
+ name = forms.CharField(required=False, label="Name")
406
+ phone = forms.CharField(required=False, label="Phone")
407
+ email = forms.CharField(required=False, label="Email")
408
+
409
+ class Meta:
410
+ model = ContactAssociation
411
+ fields = [
412
+ "action",
413
+ "location",
414
+ "contact",
415
+ "team",
416
+ "role",
417
+ "status",
418
+ "name",
419
+ "phone",
420
+ "email",
421
+ ]
422
+
423
+
374
424
  #
375
425
  # Rack groups
376
426
  #
@@ -205,6 +205,7 @@ class PathEndpoint(models.Model):
205
205
 
206
206
  @extras_features(
207
207
  "cable_terminations",
208
+ "custom_links",
208
209
  "custom_validators",
209
210
  "export_templates",
210
211
  "graphql",
@@ -236,7 +237,7 @@ class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
236
237
  #
237
238
 
238
239
 
239
- @extras_features("cable_terminations", "custom_validators", "graphql", "webhooks")
240
+ @extras_features("custom_links", "cable_terminations", "custom_validators", "graphql", "webhooks")
240
241
  class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
241
242
  """
242
243
  A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
@@ -265,6 +266,7 @@ class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
265
266
 
266
267
  @extras_features(
267
268
  "cable_terminations",
269
+ "custom_links",
268
270
  "custom_validators",
269
271
  "export_templates",
270
272
  "graphql",
@@ -380,7 +382,7 @@ class PowerPort(CableTermination, PathEndpoint, ComponentModel):
380
382
  #
381
383
 
382
384
 
383
- @extras_features("cable_terminations", "custom_validators", "graphql", "webhooks")
385
+ @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
384
386
  class PowerOutlet(CableTermination, PathEndpoint, ComponentModel):
385
387
  """
386
388
  A physical power outlet (output) within a Device which provides power to a PowerPort.
@@ -490,6 +492,7 @@ class BaseInterface(RelationshipModel):
490
492
 
491
493
  @extras_features(
492
494
  "cable_terminations",
495
+ "custom_links",
493
496
  "custom_validators",
494
497
  "export_templates",
495
498
  "graphql",
@@ -892,7 +895,7 @@ class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
892
895
  #
893
896
 
894
897
 
895
- @extras_features("cable_terminations", "custom_validators", "graphql", "webhooks")
898
+ @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
896
899
  class FrontPort(CableTermination, ComponentModel):
897
900
  """
898
901
  A pass-through port on the front of a Device.
@@ -936,7 +939,7 @@ class FrontPort(CableTermination, ComponentModel):
936
939
  )
937
940
 
938
941
 
939
- @extras_features("cable_terminations", "custom_validators", "graphql", "webhooks")
942
+ @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
940
943
  class RearPort(CableTermination, ComponentModel):
941
944
  """
942
945
  A pass-through port on the rear of a Device.
@@ -978,7 +981,7 @@ class RearPort(CableTermination, ComponentModel):
978
981
  #
979
982
 
980
983
 
981
- @extras_features("custom_validators", "graphql", "webhooks")
984
+ @extras_features("custom_links", "custom_validators", "graphql", "webhooks")
982
985
  class DeviceBay(ComponentModel):
983
986
  """
984
987
  An empty space within a Device which can house a child device
@@ -1028,6 +1031,7 @@ class DeviceBay(ComponentModel):
1028
1031
 
1029
1032
 
1030
1033
  @extras_features(
1034
+ "custom_links",
1031
1035
  "custom_validators",
1032
1036
  "export_templates",
1033
1037
  "graphql",
@@ -78,7 +78,7 @@
78
78
  </div>
79
79
  <div class="panel panel-default">
80
80
  <div class="panel-heading">
81
- <strong>Contact Info</strong>
81
+ <strong>Geographical Info</strong>
82
82
  </div>
83
83
  <table class="table table-hover panel-body attr-table">
84
84
  <tr>
@@ -115,20 +115,39 @@
115
115
  {% endif %}
116
116
  </td>
117
117
  </tr>
118
- <tr>
119
- <td>Contact Name</td>
120
- <td>{{ object.contact_name|placeholder }}</td>
121
- </tr>
122
- <tr>
123
- <td>Contact Phone</td>
124
- <td>{{ object.contact_phone|hyperlinked_phone_number }}</td>
125
- </tr>
126
- <tr>
127
- <td>Contact E-Mail</td>
128
- <td>{{ object.contact_email|hyperlinked_email }}</td>
129
- </tr>
130
118
  </table>
131
119
  </div>
120
+ {% if show_convert_to_contact_button %}
121
+ <div class="panel panel-default">
122
+ <div class="panel-heading">
123
+ <strong>Contact Info</strong>
124
+ </div>
125
+ <table class="table table-hover panel-body attr-table">
126
+ <tr>
127
+ <td>Contact Name</td>
128
+ <td>{{ object.contact_name|placeholder }}</td>
129
+ </tr>
130
+ <tr>
131
+ <td>Contact Phone</td>
132
+ <td>{{ object.contact_phone|hyperlinked_phone_number }}</td>
133
+ </tr>
134
+ <tr>
135
+ <td>Contact E-Mail</td>
136
+ <td>{{ object.contact_email|hyperlinked_email }}</td>
137
+ </tr>
138
+ </table>
139
+ {% if request.user|has_perms:contact_association_permission %}
140
+ {% with request.path|add:"?tab=contacts"|urlencode as return_url %}
141
+ <div class="panel-footer text-right noprint">
142
+ <a href="{% url 'dcim:location_migrate_data_to_contact' pk=object.pk %}?return_url={{return_url}}" class="btn btn-primary btn-xs">
143
+ <span class="mdi mdi-account-edit" aria-hidden="true"></span>
144
+ Convert to contact/team record
145
+ </a>
146
+ </div>
147
+ {% endwith %}
148
+ {% endif %}
149
+ </div>
150
+ {% endif %}
132
151
  <div class="panel panel-default">
133
152
  <div class="panel-heading">
134
153
  <strong>Comments</strong>
@@ -0,0 +1,102 @@
1
+ {% extends 'generic/object_create.html' %}
2
+ {% load form_helpers %}
3
+ {% load helpers %}
4
+
5
+ {% block title %}Migrating contact data from {{ obj_type }} {{ obj }}{% endblock %}
6
+ {% block form %}
7
+ <div id="assign" class="tabcontent">
8
+ <div class="panel panel-default">
9
+ <div class="panel-heading"><strong>Contact Data</strong></div>
10
+ <div class="panel-body">
11
+ {% render_field form.action %}
12
+ {% render_field form.location %}
13
+ <div class="form-group">
14
+ <label class="col-md-3 control-label required">Source Location "Contact Name"</label>
15
+ <div class="col-md-9">
16
+ <p class="form-control-static">{{ obj.contact_name | placeholder}}</p>
17
+ </div>
18
+ </div>
19
+ <div class="form-group">
20
+ <label class="col-md-3 control-label required">Source Location "Contact Phone"</label>
21
+ <div class="col-md-9">
22
+ <p class="form-control-static">{{ obj.contact_phone | placeholder}}</p>
23
+ </div>
24
+ </div>
25
+ <div class="form-group">
26
+ <label class="col-md-3 control-label required">Source Location "Contact Email"</label>
27
+ <div class="col-md-9">
28
+ <p class="form-control-static">{{ obj.contact_email | placeholder}}</p>
29
+ </div>
30
+ </div>
31
+ {% render_field form.name %}
32
+ {% render_field form.phone %}
33
+ {% render_field form.email %}
34
+ {% render_field form.contact %}
35
+ {% render_field form.team %}
36
+ {% render_field form.role %}
37
+ {% render_field form.status %}
38
+ </div>
39
+ </div>
40
+ </div>
41
+ {% endblock %}
42
+
43
+ {% block buttons %}
44
+ <button type="submit" name="_create" class="btn btn-primary">Assign</button>
45
+ <a href="{% url 'dcim:location' pk=obj.pk %}" class="btn btn-default">Cancel</a>
46
+ {% endblock %}
47
+
48
+ {% block javascript %}
49
+ <script>
50
+
51
+ // action drop down toggle
52
+ const action = document.getElementById("id_action");
53
+ function toggle_form_fields() {
54
+ const action_option = action.value;
55
+ document.getElementById("id_location").disabled = true;
56
+ const name = document.getElementById("id_name");
57
+ const name_label = document.querySelector("label[for=id_name]");
58
+ const phone = document.getElementById("id_phone");
59
+ const email = document.getElementById("id_email");
60
+ const similar_contacts_label = document.querySelector("label[for=id_contact]");
61
+ const similar_contacts = document.getElementById("id_contact");
62
+ const similar_teams_label = document.querySelector("label[for=id_team]");
63
+ const similar_teams = document.getElementById("id_team");
64
+
65
+ // Toggle these form fields when the action matches
66
+ similar_contacts.toggleAttribute("required", action_option=="associate existing contact");
67
+ similar_contacts_label.classList.toggle("required", action_option=="associate existing contact")
68
+ similar_teams.toggleAttribute("required", action_option=="associate existing team");
69
+ similar_teams_label.classList.toggle("required", action_option=="associate existing team")
70
+ name.toggleAttribute("disabled", action_option.match(/associate existing/));
71
+ name.toggleAttribute("required", action_option.match(/create and assign/));
72
+ name_label.classList.toggle("required", action_option.match(/create and assign/));
73
+ phone.toggleAttribute("disabled", action_option.match(/associate existing/));
74
+ email.toggleAttribute("disabled", action_option.match(/associate existing/));
75
+
76
+ // Show and hide form fields and toggle form field labels
77
+ if (action_option === "associate existing contact"){
78
+ similar_contacts.parentElement.parentElement.style.display = "block";
79
+ similar_teams.parentElement.parentElement.style.display = "none";
80
+ name.parentElement.parentElement.style.display = "none";
81
+ phone.parentElement.parentElement.style.display = "none";
82
+ email.parentElement.parentElement.style.display = "none";
83
+ } else if (action_option === "associate existing team"){
84
+ similar_teams.parentElement.parentElement.style.display = "block";
85
+ similar_contacts.parentElement.parentElement.style.display = "none";
86
+ name.parentElement.parentElement.style.display = "none";
87
+ phone.parentElement.parentElement.style.display = "none";
88
+ email.parentElement.parentElement.style.display = "none";
89
+ } else {
90
+ similar_contacts.parentElement.parentElement.style.display = "none";
91
+ similar_contacts.removeAttribute("required")
92
+ similar_teams.parentElement.parentElement.style.display = "none";
93
+ name.parentElement.parentElement.style.display = "block";
94
+ phone.parentElement.parentElement.style.display = "block";
95
+ email.parentElement.parentElement.style.display = "block";
96
+ }
97
+
98
+ }
99
+ window.onload = toggle_form_fields
100
+ action.onchange = toggle_form_fields
101
+ </script>
102
+ {% endblock %}