nautobot 2.4.1__py3-none-any.whl → 2.4.3__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 (461) hide show
  1. nautobot/circuits/templates/circuits/inc/circuit_termination.html +1 -1
  2. nautobot/circuits/tests/integration/test_circuit.py +135 -0
  3. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +43 -0
  4. nautobot/circuits/tests/integration/test_relationships.py +1 -1
  5. nautobot/circuits/views.py +4 -1
  6. nautobot/cloud/api/views.py +3 -3
  7. nautobot/core/apps/__init__.py +0 -5
  8. nautobot/core/constants.py +0 -1
  9. nautobot/core/forms/__init__.py +2 -0
  10. nautobot/core/forms/forms.py +2 -1
  11. nautobot/core/forms/widgets.py +8 -0
  12. nautobot/core/management/commands/generate_performance_test_endpoints.py +268 -0
  13. nautobot/core/templates/generic/object_bulk_delete.html +1 -1
  14. nautobot/core/templates/generic/object_bulk_destroy.html +1 -1
  15. nautobot/core/templates/generic/object_bulk_edit.html +1 -1
  16. nautobot/core/templates/generic/object_bulk_import.html +1 -1
  17. nautobot/core/templates/generic/object_create.html +5 -0
  18. nautobot/core/templates/generic/object_delete.html +1 -1
  19. nautobot/core/templates/generic/object_detail.html +1 -1
  20. nautobot/core/templates/generic/object_edit.html +1 -1
  21. nautobot/core/templates/inc/javascript.html +2 -0
  22. nautobot/core/templates/widgets/clearable_file.html +5 -0
  23. nautobot/core/templatetags/helpers.py +3 -3
  24. nautobot/core/testing/integration.py +469 -12
  25. nautobot/core/tests/test_commands.py +31 -0
  26. nautobot/core/tests/test_jobs.py +34 -2
  27. nautobot/core/tests/test_utils.py +17 -2
  28. nautobot/core/utils/git.py +7 -2
  29. nautobot/core/utils/lookup.py +12 -1
  30. nautobot/core/views/generic.py +10 -2
  31. nautobot/core/views/mixins.py +22 -7
  32. nautobot/core/views/utils.py +2 -2
  33. nautobot/dcim/api/views.py +11 -10
  34. nautobot/dcim/forms.py +15 -6
  35. nautobot/dcim/models/devices.py +1 -2
  36. nautobot/dcim/tables/devices.py +2 -1
  37. nautobot/dcim/templates/dcim/cable.html +1 -1
  38. nautobot/dcim/templates/dcim/cable_trace.html +4 -4
  39. nautobot/dcim/templates/dcim/consoleport.html +14 -4
  40. nautobot/dcim/templates/dcim/consoleserverport.html +14 -4
  41. nautobot/dcim/templates/dcim/device/base.html +1 -1
  42. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +3 -3
  43. nautobot/dcim/templates/dcim/device.html +2 -2
  44. nautobot/dcim/templates/dcim/device_component.html +1 -1
  45. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  46. nautobot/dcim/templates/dcim/frontport.html +7 -2
  47. nautobot/dcim/templates/dcim/interface.html +9 -4
  48. nautobot/dcim/templates/dcim/location.html +1 -1
  49. nautobot/dcim/templates/dcim/locationtype.html +1 -1
  50. nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
  51. nautobot/dcim/templates/dcim/manufacturer.html +1 -1
  52. nautobot/dcim/templates/dcim/platform.html +1 -1
  53. nautobot/dcim/templates/dcim/powerfeed.html +9 -4
  54. nautobot/dcim/templates/dcim/poweroutlet.html +14 -4
  55. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  56. nautobot/dcim/templates/dcim/powerport.html +14 -4
  57. nautobot/dcim/templates/dcim/rack.html +1 -1
  58. nautobot/dcim/templates/dcim/rackgroup.html +1 -1
  59. nautobot/dcim/templates/dcim/rackreservation.html +2 -2
  60. nautobot/dcim/templates/dcim/rearport.html +7 -2
  61. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  62. nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
  63. nautobot/dcim/tests/integration/test_fileinputpicker.py +87 -0
  64. nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
  65. nautobot/dcim/tests/test_models.py +1 -1
  66. nautobot/dcim/tests/test_views.py +9 -1
  67. nautobot/dcim/views.py +12 -15
  68. nautobot/extras/api/serializers.py +33 -0
  69. nautobot/extras/api/views.py +13 -5
  70. nautobot/extras/constants.py +1 -0
  71. nautobot/extras/datasources/git.py +125 -0
  72. nautobot/extras/forms/forms.py +4 -0
  73. nautobot/extras/jobs.py +8 -1
  74. nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
  75. nautobot/extras/models/customfields.py +29 -12
  76. nautobot/extras/models/datasources.py +85 -0
  77. nautobot/extras/models/models.py +15 -0
  78. nautobot/extras/models/relationships.py +17 -5
  79. nautobot/extras/signals.py +15 -1
  80. nautobot/extras/templates/extras/computedfield.html +1 -1
  81. nautobot/extras/templates/extras/configcontext.html +1 -1
  82. nautobot/extras/templates/extras/configcontextschema.html +1 -1
  83. nautobot/extras/templates/extras/customfield.html +1 -1
  84. nautobot/extras/templates/extras/customlink.html +1 -1
  85. nautobot/extras/templates/extras/dynamicgroup.html +1 -1
  86. nautobot/extras/templates/extras/exporttemplate.html +1 -1
  87. nautobot/extras/templates/extras/gitrepository.html +1 -1
  88. nautobot/extras/templates/extras/graphqlquery.html +1 -1
  89. nautobot/extras/templates/extras/job.html +1 -0
  90. nautobot/extras/templates/extras/job_detail.html +1 -1
  91. nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
  92. nautobot/extras/templates/extras/jobhook.html +1 -1
  93. nautobot/extras/templates/extras/jobresult.html +1 -1
  94. nautobot/extras/templates/extras/objectchange.html +1 -1
  95. nautobot/extras/templates/extras/plugin_detail.html +1 -1
  96. nautobot/extras/templates/extras/relationship.html +1 -63
  97. nautobot/extras/templates/extras/role_retrieve.html +1 -1
  98. nautobot/extras/templates/extras/scheduledjob.html +1 -1
  99. nautobot/extras/templates/extras/secret.html +1 -1
  100. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  101. nautobot/extras/templates/extras/status.html +1 -1
  102. nautobot/extras/templates/extras/tag.html +1 -1
  103. nautobot/extras/templates/extras/webhook.html +1 -1
  104. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
  105. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
  106. nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
  107. nautobot/extras/tests/git_helper.py +9 -1
  108. nautobot/extras/tests/integration/__init__.py +29 -16
  109. nautobot/extras/tests/test_api.py +6 -0
  110. nautobot/extras/tests/test_customfields.py +49 -51
  111. nautobot/extras/tests/test_datasources.py +27 -0
  112. nautobot/extras/tests/test_dynamicgroups.py +14 -0
  113. nautobot/extras/tests/test_models.py +283 -0
  114. nautobot/extras/tests/test_utils.py +22 -1
  115. nautobot/extras/tests/test_views.py +197 -9
  116. nautobot/extras/utils.py +47 -8
  117. nautobot/extras/views.py +84 -26
  118. nautobot/ipam/api/views.py +3 -3
  119. nautobot/ipam/forms.py +2 -6
  120. nautobot/ipam/models.py +8 -2
  121. nautobot/ipam/tables.py +2 -2
  122. nautobot/ipam/templates/ipam/ipaddress.html +1 -1
  123. nautobot/ipam/templates/ipam/prefix.html +1 -1
  124. nautobot/ipam/templates/ipam/rir.html +1 -1
  125. nautobot/ipam/templates/ipam/routetarget.html +1 -1
  126. nautobot/ipam/templates/ipam/service.html +1 -1
  127. nautobot/ipam/templates/ipam/vlan.html +1 -1
  128. nautobot/ipam/templates/ipam/vlangroup.html +1 -1
  129. nautobot/ipam/templates/ipam/vrf.html +1 -1
  130. nautobot/ipam/tests/test_models.py +24 -0
  131. nautobot/ipam/tests/test_utils.py +41 -2
  132. nautobot/ipam/utils/__init__.py +18 -11
  133. nautobot/project-static/bootstrap-filestyle-1.2.3/bootstrap-filestyle.min.js +11 -0
  134. nautobot/project-static/docs/404.html +87 -12
  135. nautobot/project-static/docs/apps/index.html +88 -13
  136. nautobot/project-static/docs/apps/nautobot-apps.html +88 -13
  137. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.60a45f97.min.js} +1 -1
  138. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.60a45f97.min.js.map} +1 -1
  139. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
  140. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
  141. nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
  142. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +87 -12
  143. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +87 -12
  144. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +87 -12
  145. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +87 -12
  146. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +87 -12
  147. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +87 -12
  148. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +87 -12
  149. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +87 -12
  150. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +87 -12
  151. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +87 -12
  152. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +87 -12
  153. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +87 -12
  154. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +87 -12
  155. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +87 -12
  156. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +87 -12
  157. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +87 -12
  158. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +87 -12
  159. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +87 -12
  160. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +87 -12
  161. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +87 -12
  162. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +87 -12
  163. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +87 -12
  164. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +177 -20
  165. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +114 -17
  166. nautobot/project-static/docs/development/apps/api/configuration-view.html +87 -12
  167. nautobot/project-static/docs/development/apps/api/database-backend-config.html +87 -12
  168. nautobot/project-static/docs/development/apps/api/models/django-admin.html +87 -12
  169. nautobot/project-static/docs/development/apps/api/models/global-search.html +87 -12
  170. nautobot/project-static/docs/development/apps/api/models/graphql.html +96 -21
  171. nautobot/project-static/docs/development/apps/api/models/index.html +87 -12
  172. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +87 -12
  173. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +87 -12
  174. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +89 -14
  175. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +87 -12
  176. nautobot/project-static/docs/development/apps/api/platform-features/index.html +87 -12
  177. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +87 -12
  178. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +87 -12
  179. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +87 -12
  180. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +87 -12
  181. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +87 -12
  182. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +87 -12
  183. nautobot/project-static/docs/development/apps/api/prometheus.html +87 -12
  184. nautobot/project-static/docs/development/apps/api/setup.html +88 -13
  185. nautobot/project-static/docs/development/apps/api/testing.html +87 -12
  186. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +87 -12
  187. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +87 -12
  188. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +87 -12
  189. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +87 -12
  190. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +87 -12
  191. nautobot/project-static/docs/development/apps/api/views/base-template.html +87 -12
  192. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +87 -12
  193. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +87 -12
  194. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +87 -12
  195. nautobot/project-static/docs/development/apps/api/views/index.html +87 -12
  196. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +87 -12
  197. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +87 -12
  198. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +87 -12
  199. nautobot/project-static/docs/development/apps/api/views/notes.html +87 -12
  200. nautobot/project-static/docs/development/apps/api/views/rest-api.html +87 -12
  201. nautobot/project-static/docs/development/apps/api/views/urls.html +87 -12
  202. nautobot/project-static/docs/development/apps/index.html +87 -12
  203. nautobot/project-static/docs/development/apps/migration/code-updates.html +93 -17
  204. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +89 -14
  205. nautobot/project-static/docs/development/apps/migration/from-v1.html +90 -15
  206. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +87 -12
  207. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +87 -12
  208. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +87 -12
  209. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +87 -12
  210. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +87 -12
  211. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +87 -12
  212. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +88 -13
  213. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +87 -12
  214. nautobot/project-static/docs/development/apps/porting-from-netbox.html +87 -12
  215. nautobot/project-static/docs/development/core/application-registry.html +87 -12
  216. nautobot/project-static/docs/development/core/best-practices.html +88 -13
  217. nautobot/project-static/docs/development/core/bootstrap-ui.html +88 -13
  218. nautobot/project-static/docs/development/core/caching.html +87 -12
  219. nautobot/project-static/docs/development/core/controllers.html +87 -12
  220. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +94 -19
  221. nautobot/project-static/docs/development/core/generic-views.html +87 -12
  222. nautobot/project-static/docs/development/core/getting-started.html +89 -14
  223. nautobot/project-static/docs/development/core/homepage.html +87 -12
  224. nautobot/project-static/docs/development/core/index.html +88 -13
  225. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +90 -15
  226. nautobot/project-static/docs/development/core/model-checklist.html +88 -13
  227. nautobot/project-static/docs/development/core/model-features.html +87 -12
  228. nautobot/project-static/docs/development/core/natural-keys.html +87 -12
  229. nautobot/project-static/docs/development/core/navigation-menu.html +88 -13
  230. nautobot/project-static/docs/development/core/release-checklist.html +88 -13
  231. nautobot/project-static/docs/development/core/role-internals.html +87 -12
  232. nautobot/project-static/docs/development/core/settings.html +88 -13
  233. nautobot/project-static/docs/development/core/style-guide.html +91 -16
  234. nautobot/project-static/docs/development/core/templates.html +88 -13
  235. nautobot/project-static/docs/development/core/testing.html +87 -12
  236. nautobot/project-static/docs/development/core/ui-component-framework.html +87 -12
  237. nautobot/project-static/docs/development/core/user-preferences.html +87 -12
  238. nautobot/project-static/docs/development/index.html +87 -12
  239. nautobot/project-static/docs/development/jobs/index.html +95 -13
  240. nautobot/project-static/docs/development/jobs/migration/from-v1.html +90 -14
  241. nautobot/project-static/docs/index.html +90 -14
  242. nautobot/project-static/docs/objects.inv +0 -0
  243. nautobot/project-static/docs/overview/application_stack.html +89 -14
  244. nautobot/project-static/docs/overview/design_philosophy.html +87 -12
  245. nautobot/project-static/docs/release-notes/index.html +87 -12
  246. nautobot/project-static/docs/release-notes/version-1.0.html +89 -14
  247. nautobot/project-static/docs/release-notes/version-1.1.html +89 -14
  248. nautobot/project-static/docs/release-notes/version-1.2.html +90 -15
  249. nautobot/project-static/docs/release-notes/version-1.3.html +88 -13
  250. nautobot/project-static/docs/release-notes/version-1.4.html +104 -29
  251. nautobot/project-static/docs/release-notes/version-1.5.html +95 -20
  252. nautobot/project-static/docs/release-notes/version-1.6.html +91 -16
  253. nautobot/project-static/docs/release-notes/version-2.0.html +97 -22
  254. nautobot/project-static/docs/release-notes/version-2.1.html +94 -19
  255. nautobot/project-static/docs/release-notes/version-2.2.html +88 -13
  256. nautobot/project-static/docs/release-notes/version-2.3.html +91 -16
  257. nautobot/project-static/docs/release-notes/version-2.4.html +465 -12
  258. nautobot/project-static/docs/requirements.txt +1 -1
  259. nautobot/project-static/docs/search/search_index.json +1 -1
  260. nautobot/project-static/docs/sitemap.xml +296 -288
  261. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  262. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +90 -15
  263. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +87 -12
  264. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +91 -16
  265. nautobot/project-static/docs/user-guide/administration/configuration/index.html +87 -12
  266. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +88 -13
  267. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +90 -15
  268. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +87 -12
  269. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +95 -20
  270. nautobot/project-static/docs/user-guide/administration/guides/docker.html +90 -15
  271. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +88 -13
  272. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +87 -12
  273. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +91 -16
  274. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +87 -12
  275. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +102 -27
  276. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +89 -14
  277. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +87 -12
  278. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +88 -13
  279. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +87 -12
  280. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +87 -12
  281. nautobot/project-static/docs/user-guide/administration/installation/index.html +87 -12
  282. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +88 -13
  283. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +93 -18
  284. nautobot/project-static/docs/user-guide/administration/installation/services.html +88 -13
  285. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +87 -12
  286. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +87 -12
  287. nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
  288. nautobot/project-static/docs/user-guide/administration/security/notices.html +9844 -0
  289. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +87 -12
  290. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +88 -13
  291. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +87 -12
  292. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +87 -12
  293. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +87 -12
  294. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +87 -12
  295. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +87 -12
  296. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +87 -12
  297. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +87 -12
  298. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +98 -20
  299. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +87 -12
  300. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +87 -12
  301. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +87 -12
  302. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +87 -12
  303. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +87 -12
  304. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +87 -12
  305. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +87 -12
  306. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +87 -12
  307. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +87 -12
  308. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +87 -12
  309. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +87 -12
  310. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +87 -12
  311. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +87 -12
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +87 -12
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +87 -12
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +87 -12
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +87 -12
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +87 -12
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +87 -12
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +87 -12
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +87 -12
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +87 -12
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +87 -12
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +87 -12
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +87 -12
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +87 -12
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +87 -12
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +87 -12
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +87 -12
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +87 -12
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +87 -12
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +87 -12
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +87 -12
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +87 -12
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +87 -12
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +87 -12
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +87 -12
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +87 -12
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +87 -12
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +87 -12
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +87 -12
  340. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +87 -12
  341. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +87 -12
  342. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +87 -12
  343. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +87 -12
  344. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +87 -12
  345. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +87 -12
  346. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +87 -12
  347. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +87 -12
  348. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +87 -12
  349. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +87 -12
  350. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +87 -12
  351. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +87 -12
  352. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +87 -12
  353. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +87 -12
  354. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +87 -12
  355. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +87 -12
  356. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +87 -12
  357. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +87 -12
  358. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +87 -12
  359. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +87 -12
  360. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +87 -12
  361. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +87 -12
  362. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +87 -12
  363. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +87 -12
  364. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +87 -12
  365. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +87 -12
  366. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +87 -12
  367. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +87 -12
  368. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +87 -12
  369. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +87 -12
  370. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +87 -12
  371. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +87 -12
  372. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +87 -12
  373. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +87 -12
  374. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +87 -12
  375. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +87 -12
  376. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +87 -12
  377. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +87 -12
  378. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +87 -12
  379. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +87 -12
  380. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +99 -24
  381. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +87 -12
  382. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +87 -12
  383. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +90 -15
  384. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +87 -12
  385. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +87 -12
  386. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +87 -12
  387. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +87 -12
  388. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +87 -12
  389. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +87 -12
  390. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +188 -30
  391. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +87 -12
  392. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +87 -12
  393. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +87 -12
  394. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +87 -12
  395. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +87 -12
  396. nautobot/project-static/docs/user-guide/index.html +87 -12
  397. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +87 -12
  398. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +87 -12
  399. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +87 -12
  400. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +87 -12
  401. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +88 -13
  402. nautobot/project-static/docs/user-guide/platform-functionality/events.html +87 -12
  403. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +87 -12
  404. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +87 -12
  405. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +407 -14
  406. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +90 -15
  407. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +87 -12
  408. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +87 -12
  409. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +87 -12
  410. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +87 -12
  411. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +87 -12
  412. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +87 -12
  413. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +89 -14
  414. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +93 -18
  415. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +87 -12
  416. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +87 -12
  417. nautobot/project-static/docs/user-guide/platform-functionality/note.html +87 -12
  418. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +87 -12
  419. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +87 -12
  420. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +87 -12
  421. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +87 -12
  422. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +87 -12
  423. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +87 -12
  424. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +87 -12
  425. nautobot/project-static/docs/user-guide/platform-functionality/role.html +87 -12
  426. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +87 -12
  427. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +87 -12
  428. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +87 -12
  429. nautobot/project-static/docs/user-guide/platform-functionality/status.html +87 -12
  430. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +87 -12
  431. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +87 -12
  432. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +87 -12
  433. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +87 -12
  434. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +87 -12
  435. nautobot/project-static/js/dropdown.js +28 -0
  436. nautobot/tenancy/forms.py +9 -0
  437. nautobot/tenancy/templates/tenancy/tenant.html +1 -2
  438. nautobot/tenancy/templates/tenancy/tenant_create.html +21 -0
  439. nautobot/tenancy/templates/tenancy/tenant_edit.html +2 -21
  440. nautobot/tenancy/templates/tenancy/tenantgroup.html +2 -44
  441. nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +1 -0
  442. nautobot/tenancy/tests/test_views.py +5 -1
  443. nautobot/tenancy/urls.py +7 -79
  444. nautobot/tenancy/views.py +51 -80
  445. nautobot/virtualization/templates/virtualization/cluster.html +1 -1
  446. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
  447. nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
  448. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  449. nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
  450. nautobot/wireless/api/serializers.py +6 -1
  451. nautobot/wireless/api/views.py +3 -3
  452. nautobot/wireless/tests/test_api.py +5 -0
  453. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/METADATA +12 -12
  454. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/RECORD +459 -443
  455. nautobot/dcim/tests/integration/test_device_bulk_delete.py +0 -189
  456. nautobot/dcim/tests/integration/test_device_bulk_edit.py +0 -181
  457. /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
  458. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/LICENSE.txt +0 -0
  459. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/NOTICE +0 -0
  460. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/WHEEL +0 -0
  461. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/entry_points.txt +0 -0
@@ -6,7 +6,7 @@ In a future release, we should delete this file.
6
6
 
7
7
  <div class="panel panel-default">
8
8
  <div class="panel-heading">
9
- {% include 'circuits/inc/circuit_termination_header_extra_content.html' with termination=termination %}
9
+ {% include 'circuits/inc/circuit_termination_header_extra_content.html' with termination=termination side=side %}
10
10
  <strong>Termination - {{ side }} Side</strong>
11
11
  </div>
12
12
  {% if termination %}
@@ -0,0 +1,135 @@
1
+ from django.contrib.contenttypes.models import ContentType
2
+ from django.urls import reverse
3
+
4
+ from nautobot.circuits.models import Circuit, CircuitTermination, CircuitType, Provider
5
+ from nautobot.core.testing.integration import ObjectDetailsMixin, ObjectsListMixin, SeleniumTestCase
6
+ from nautobot.dcim.models import Location, LocationType
7
+ from nautobot.extras.models import Status
8
+ from nautobot.tenancy.models import Tenant, TenantGroup
9
+
10
+
11
+ class CircuitTestCase(SeleniumTestCase, ObjectsListMixin, ObjectDetailsMixin):
12
+ """
13
+ Integration test to check circuits related test cases.
14
+ """
15
+
16
+ def setUp(self):
17
+ super().setUp()
18
+ self.login_as_superuser()
19
+
20
+ # Termination requirements
21
+ location_type, location_created = LocationType.objects.get_or_create(name="Circuit at Home")
22
+ if location_created:
23
+ location_ct = ContentType.objects.get_for_model(CircuitTermination)
24
+ location_type.content_types.set([location_ct])
25
+ location_type.save()
26
+
27
+ location_status = Status.objects.get_for_model(Location).first()
28
+ self.location, _ = Location.objects.get_or_create(
29
+ name="A Test Location",
30
+ status=location_status,
31
+ location_type=location_type,
32
+ )
33
+
34
+ self.provider, _ = Provider.objects.get_or_create(name="World Best Cables")
35
+ self.circuit_type, _ = CircuitType.objects.get_or_create(
36
+ name="Yellow Cable",
37
+ )
38
+ self.tenant_group, _ = TenantGroup.objects.get_or_create(name="Family Inc.")
39
+ self.tenant, _ = Tenant.objects.get_or_create(name="Tenant 1", tenant_group=self.tenant_group)
40
+
41
+ def create_circuit(self, name):
42
+ circuit, _ = Circuit.objects.get_or_create(
43
+ provider=self.provider,
44
+ cid=name,
45
+ circuit_type=self.circuit_type,
46
+ status=Status.objects.get_for_model(Circuit).first(),
47
+ )
48
+ return circuit
49
+
50
+ def test_circuit_create(self):
51
+ cid = "Circuit-test-abc123"
52
+ description = "My Precious Circuit"
53
+
54
+ # Create new Circuit from list view
55
+ self.click_navbar_entry("Circuits", "Circuits")
56
+ self.assertEqual(self.browser.url, self.live_server_url + reverse("circuits:circuit_list"))
57
+
58
+ self.click_list_view_add_button()
59
+ self.assertEqual(self.browser.url, self.live_server_url + reverse("circuits:circuit_add"))
60
+
61
+ # Fill Circuit creation form
62
+ self.fill_select2_field("provider", self.provider.name)
63
+ self.browser.fill("cid", cid)
64
+ self.fill_select2_field("circuit_type", self.circuit_type.name)
65
+ self.fill_select2_field("status", "") # pick first status
66
+ self.browser.fill("install_date", "2025-01-01")
67
+ self.browser.fill("commit_rate", 192)
68
+ self.browser.fill("description", "My Precious Circuit")
69
+ self.fill_select2_field("tenant_group", "Family Inc.")
70
+ self.fill_select2_field("tenant", "Tenant 1")
71
+
72
+ self.click_edit_form_create_button()
73
+ self.assertTrue(self.browser.is_text_present("Created circuit Circuit-test-abc123"))
74
+
75
+ # Navigate to Circuit details by filtering the one just created and clicking on it
76
+ self.click_navbar_entry("Circuits", "Circuits")
77
+ self.assertEqual(self.browser.url, self.live_server_url + reverse("circuits:circuit_list"))
78
+
79
+ self.apply_filter("circuit_type", "Yellow Cable")
80
+ self.assertEqual(self.objects_list_visible_items, 1)
81
+
82
+ self.click_table_link()
83
+
84
+ # Assert that value are properly set
85
+ circuit = Circuit.objects.get(cid=cid)
86
+ self.assertIn(self.live_server_url + reverse("circuits:circuit", kwargs={"pk": circuit.pk}), self.browser.url)
87
+
88
+ self.assertPanelValue("Circuit", "Circuit ID", cid)
89
+ self.assertPanelValue("Circuit", "Status", "Active")
90
+ self.assertPanelValue("Circuit", "Provider", self.provider.name)
91
+ self.assertPanelValue("Circuit", "Circuit Type", self.circuit_type.name)
92
+ self.assertPanelValue("Circuit", "Tenant", self.tenant.name)
93
+ self.assertPanelValue("Circuit", "Date Installed", "Jan. 1, 2025")
94
+ self.assertPanelValue("Circuit", "Commit Rate (Kbps)", "192")
95
+ self.assertPanelValue("Circuit", "Description", description)
96
+
97
+ def test_circuit_create_termination(self):
98
+ circuit = self.create_circuit("Circuit-test-termination")
99
+ sides = ["A", "Z"]
100
+ for side in sides:
101
+ with self.subTest(side=side):
102
+ # Go to Circuit details page
103
+ details_url = self.live_server_url + reverse("circuits:circuit", kwargs={"pk": circuit.pk})
104
+ self.browser.visit(details_url)
105
+ self.assertIn(details_url, self.browser.url)
106
+
107
+ # Find and click add termination button
108
+ termination_panel_xpath = f'//*[@id="main"]//div[@class="panel-heading"][contains(normalize-space(), "Termination - {side} Side")]'
109
+ self.browser.find_by_xpath(f'{termination_panel_xpath}//a[normalize-space()="Add"]').click()
110
+
111
+ port_speed = ord(side)
112
+ upstream_speed = ord(side) + 10
113
+ xconnect_id = f"xconnect-id-{side}-123"
114
+ pp_info = f"pp-info-{side}-123"
115
+ description = f"{side}-side"
116
+
117
+ # Fill termination creation form
118
+ self.fill_select2_field("location", self.location.name)
119
+ self.browser.fill("port_speed", port_speed)
120
+ self.browser.fill("upstream_speed", upstream_speed)
121
+ self.browser.fill("xconnect_id", xconnect_id)
122
+ self.browser.fill("pp_info", pp_info)
123
+ self.browser.fill("description", description)
124
+ self.click_edit_form_create_button()
125
+
126
+ self.assertTrue(self.browser.is_text_present(f"Created circuit termination Termination {side}"))
127
+
128
+ # Assert that value are properly set
129
+ panel_label = f"Termination - {side} Side"
130
+ self.assertPanelValue(panel_label, "Location", self.location.name)
131
+ self.assertPanelValue(panel_label, "Port Speed (Kbps)", port_speed)
132
+ self.assertPanelValue(panel_label, "Upstream Speed (Kbps)", upstream_speed)
133
+ self.assertPanelValue(panel_label, "Cross-connect ID", xconnect_id)
134
+ self.assertPanelValue(panel_label, "Patch Panel/port(s)", pp_info)
135
+ self.assertPanelValue(panel_label, "Description", description)
@@ -0,0 +1,43 @@
1
+ import uuid
2
+
3
+ from nautobot.circuits.models import Circuit, CircuitType, Provider
4
+ from nautobot.core.testing.integration import (
5
+ BulkOperationsTestCases,
6
+ )
7
+ from nautobot.extras.models import Status
8
+
9
+
10
+ class CircuitBulkOperationsTestCase(BulkOperationsTestCases.BulkOperationsTestCase):
11
+ """
12
+ Test circuits bulk edit / delete operations.
13
+ """
14
+
15
+ model_menu_path = ("Circuits", "Circuits")
16
+ model_base_viewname = "circuits:circuit"
17
+ model_edit_data = {"commit_rate": "12345"}
18
+ model_filter_by = {"circuit_type": "Copper"}
19
+ model_class = Circuit
20
+
21
+ def setup_items(self):
22
+ Circuit.objects.all().delete()
23
+
24
+ # Create locations for test
25
+ self.create_circuit()
26
+ self.create_circuit()
27
+ self.create_circuit()
28
+ self.create_circuit("Copper")
29
+ self.create_circuit("Copper")
30
+
31
+ @staticmethod
32
+ def create_circuit(circuit_type="Fiber"):
33
+ circuit_id = f"TestCircuit-{str(uuid.uuid4())[:6]}"
34
+ provider, _ = Provider.objects.get_or_create(name="A Test Provider")
35
+ circuit_type, _ = CircuitType.objects.get_or_create(name=circuit_type)
36
+
37
+ circuit_status = Status.objects.get_for_model(Circuit).first()
38
+ Circuit.objects.get_or_create(
39
+ cid=circuit_id,
40
+ provider=provider,
41
+ status=circuit_status,
42
+ circuit_type=circuit_type,
43
+ )
@@ -19,7 +19,7 @@ class CircuitRelationshipsTestCase(SeleniumTestCase):
19
19
  self.user.is_superuser = True
20
20
  self.user.save()
21
21
  self.login(self.user.username, self.password)
22
- location_type = LocationType.objects.get(name="Campus")
22
+ location_type, _ = LocationType.objects.get_or_create(name="Campus")
23
23
  location_ct = ContentType.objects.get_for_model(Location)
24
24
  circuit_termination_ct = ContentType.objects.get_for_model(CircuitTermination)
25
25
  provider_ct = ContentType.objects.get_for_model(Provider)
@@ -134,6 +134,7 @@ class CircuitUIViewSet(NautobotUIViewSet):
134
134
 
135
135
  class CircuitTerminationPanel(ObjectFieldsPanel):
136
136
  def __init__(self, **kwargs):
137
+ self.side = kwargs.pop("side")
137
138
  super().__init__(
138
139
  fields=(
139
140
  "location", # TODO: render location hierarchy
@@ -161,7 +162,7 @@ class CircuitUIViewSet(NautobotUIViewSet):
161
162
  return True
162
163
 
163
164
  def get_extra_context(self, context):
164
- return {"termination": context[self.context_object_key]}
165
+ return {"termination": context[self.context_object_key], "side": self.side}
165
166
 
166
167
  def get_data(self, context):
167
168
  """
@@ -237,12 +238,14 @@ class CircuitUIViewSet(NautobotUIViewSet):
237
238
  section=SectionChoices.RIGHT_HALF,
238
239
  weight=100,
239
240
  context_object_key="circuit_termination_a",
241
+ side=CircuitTerminationSideChoices.SIDE_A,
240
242
  ),
241
243
  CircuitTerminationPanel(
242
244
  label="Termination - Z Side",
243
245
  section=SectionChoices.RIGHT_HALF,
244
246
  weight=200,
245
247
  context_object_key="circuit_termination_z",
248
+ side=CircuitTerminationSideChoices.SIDE_Z,
246
249
  ),
247
250
  ),
248
251
  )
@@ -1,5 +1,5 @@
1
1
  from nautobot.cloud import filters, models
2
- from nautobot.extras.api.views import NautobotModelViewSet
2
+ from nautobot.extras.api.views import ModelViewSet, NautobotModelViewSet
3
3
 
4
4
  from . import serializers
5
5
 
@@ -26,7 +26,7 @@ class CloudNetworkViewSet(NautobotModelViewSet):
26
26
  filterset_class = filters.CloudNetworkFilterSet
27
27
 
28
28
 
29
- class CloudNetworkPrefixAssignmentViewSet(NautobotModelViewSet):
29
+ class CloudNetworkPrefixAssignmentViewSet(ModelViewSet):
30
30
  queryset = models.CloudNetworkPrefixAssignment.objects.all()
31
31
  serializer_class = serializers.CloudNetworkPrefixAssignmentSerializer
32
32
  filterset_class = filters.CloudNetworkPrefixAssignmentFilterSet
@@ -38,7 +38,7 @@ class CloudServiceViewSet(NautobotModelViewSet):
38
38
  filterset_class = filters.CloudServiceFilterSet
39
39
 
40
40
 
41
- class CloudServiceNetworkAssignmentViewSet(NautobotModelViewSet):
41
+ class CloudServiceNetworkAssignmentViewSet(ModelViewSet):
42
42
  queryset = models.CloudServiceNetworkAssignment.objects.all()
43
43
  serializer_class = serializers.CloudServiceNetworkAssignmentSerializer
44
44
  filterset_class = filters.CloudServiceNetworkAssignmentFilterSet
@@ -367,11 +367,6 @@ class CoreConfig(NautobotConfig):
367
367
 
368
368
  super().ready()
369
369
 
370
- # Register jobs last after everything else has been done.
371
- from nautobot.core.celery import import_jobs
372
-
373
- import_jobs()
374
-
375
370
 
376
371
  class NautobotConstanceConfig(ConstanceConfig):
377
372
  """Override "Constance" app name to "Configuration"."""
@@ -64,7 +64,6 @@ HTML_ALLOWED_TAGS = nh3.ALLOWED_TAGS - {
64
64
  # at present we just copy nh3.ALLOWED_ATTRIBUTES but we can modify this later as desired and appropriate
65
65
  HTML_ALLOWED_ATTRIBUTES = deepcopy(nh3.ALLOWED_ATTRIBUTES)
66
66
 
67
-
68
67
  #
69
68
  # Reserved Names
70
69
  #
@@ -61,6 +61,7 @@ from nautobot.core.forms.widgets import (
61
61
  APISelect,
62
62
  APISelectMultiple,
63
63
  BulkEditNullBooleanSelect,
64
+ ClearableFileInput,
64
65
  ColorSelect,
65
66
  ContentTypeSelect,
66
67
  DatePicker,
@@ -99,6 +100,7 @@ __all__ = (
99
100
  "CSVModelForm",
100
101
  "CSVMultipleChoiceField",
101
102
  "CSVMultipleContentTypeField",
103
+ "ClearableFileInput",
102
104
  "ColorSelect",
103
105
  "CommentField",
104
106
  "ConfirmationForm",
@@ -10,6 +10,7 @@ from django.forms import formset_factory
10
10
  from django.urls import reverse
11
11
  import yaml
12
12
 
13
+ from nautobot.core.forms import widgets as nautobot_widgets
13
14
  from nautobot.core.utils.filtering import build_lookup_label, get_filter_field_label, get_filterset_parameter_form_field
14
15
  from nautobot.ipam import formfields
15
16
 
@@ -76,9 +77,9 @@ class BootstrapMixin(forms.BaseForm):
76
77
 
77
78
  exempt_widgets = [
78
79
  forms.CheckboxInput,
79
- forms.ClearableFileInput,
80
80
  forms.FileInput,
81
81
  forms.RadioSelect,
82
+ nautobot_widgets.ClearableFileInput,
82
83
  ]
83
84
 
84
85
  for field in self.fields.values():
@@ -13,6 +13,7 @@ __all__ = (
13
13
  "APISelect",
14
14
  "APISelectMultiple",
15
15
  "BulkEditNullBooleanSelect",
16
+ "ClearableFileInput",
16
17
  "ColorSelect",
17
18
  "ContentTypeSelect",
18
19
  "DatePicker",
@@ -256,3 +257,10 @@ class MultiValueCharInput(StaticSelect2Multiple):
256
257
  def __init__(self, *args, **kwargs):
257
258
  super().__init__(*args, **kwargs)
258
259
  self.attrs["class"] = "nautobot-select2-multi-value-char"
260
+
261
+
262
+ class ClearableFileInput(forms.ClearableFileInput):
263
+ template_name = "widgets/clearable_file.html"
264
+
265
+ class Media:
266
+ js = ["bootstrap-filestyle-1.2.3/bootstrap-filestyle.min.js"]
@@ -0,0 +1,268 @@
1
+ from typing import Optional
2
+
3
+ from django.conf import settings
4
+ from django.core.management.base import BaseCommand
5
+ from django.urls import get_resolver
6
+ from django.utils.http import urlencode
7
+ import yaml
8
+
9
+ from nautobot.core.utils.lookup import get_model_for_view_name
10
+
11
+ # List of view names that are excluded for various error responses
12
+ EXCLUDED_VIEW_NAMES = [
13
+ "graphql-api", # "Method \\"GET\\" not allowed."
14
+ "graphql", # "Must provide query string."
15
+ "dcim-api:device-napalm", # "No platform is configured for this device."
16
+ "dcim-api:connected-device-list", # "Request must include \\"peer_device\\" and \\"peer_interface\\" filters."
17
+ "login",
18
+ "logout",
19
+ ]
20
+
21
+ # List of reversed url name suffixes that are used to identify GET endpoints UI and API
22
+ GET_ENDPOINT_SUFFIXES = ("_list", "_notes", "_changelog", "-detail", "-list", "-notes")
23
+
24
+
25
+ class Command(BaseCommand):
26
+ """
27
+ Example usage: `nautobot-server generate_performance_test_endpoints > endpoints.yml`
28
+ """
29
+
30
+ help = "List all relevant performance test url patterns in Nautobot Core"
31
+
32
+ def add_arguments(self, parser):
33
+ parser.add_argument(
34
+ "--output-file",
35
+ help="A file path string that specifies the output file to write the endpoints to.",
36
+ )
37
+
38
+ def handle(self, *args, **options):
39
+ # Get the URL resolver
40
+ url_patterns = get_resolver().url_patterns
41
+
42
+ # Group the urls by app names
43
+ self.app_name_to_urls = {}
44
+ self.app_name_to_urls["endpoints"] = {}
45
+ # Fetch and store the urls by app names in the dictionary
46
+ self.fetch_urls(url_patterns)
47
+ for view_name, url_patterns in self.app_name_to_urls["endpoints"].items():
48
+ # De-duplicate the URL patterns and sort them.
49
+ self.app_name_to_urls["endpoints"][view_name] = sorted(list(set(url_patterns)))
50
+
51
+ if filepath := options.get("output_file"):
52
+ # Output the endpoints to a yaml file
53
+ with open(filepath, "w") as outfile:
54
+ yaml.dump(self.app_name_to_urls, outfile, sort_keys=True)
55
+ else:
56
+ # Output the endpoints to the console
57
+ self.stdout.write(yaml.dump(self.app_name_to_urls, sort_keys=True))
58
+
59
+ def is_eligible_get_endpoint(self, view_name):
60
+ """
61
+ Check if the view is a GET endpoint and if it is eligible for performance testing.
62
+ """
63
+ if view_name not in EXCLUDED_VIEW_NAMES and (view_name.endswith(GET_ENDPOINT_SUFFIXES) or "_" not in view_name):
64
+ return True
65
+ return False
66
+
67
+ def append_urls_to_dict(self, url_pattern, model_class, view_name, is_api_endpoint=False):
68
+ """
69
+ URL patterns are stored in the dictionary in the following format:
70
+ - Any model detail view URL pattern that contains `<uuid:pk>` or `(?P<pk>[/.]+)` will have two endpoints:
71
+ - One with the `model_class.objects.first().pk`
72
+ - One with the `model_class.objects.last().pk`
73
+ - Any model list view URL pattern will have two endpoints:
74
+ - One with default pagination
75
+ - One with custom pagination (5 pages with <total_object_count//5> instances per page)
76
+ - Any generic endpoint like `core:home` will have one endpoint which is the URL pattern itself.
77
+ """
78
+ if not model_class:
79
+ # A generic endpoint like `core:home`
80
+ if view_name not in self.app_name_to_urls["endpoints"]:
81
+ self.app_name_to_urls["endpoints"][view_name] = []
82
+ self.app_name_to_urls["endpoints"][view_name].append(url_pattern)
83
+ return
84
+
85
+ # Handle detail view url patterns
86
+ total_count = len(model_class.objects.all())
87
+ if "_list" not in view_name and "-list" not in view_name:
88
+ # If the model class is found, then we know we are dealing with a model related endpoint
89
+ if total_count == 0:
90
+ # TODO handle the case where there is no instances of the model is found
91
+ self.stderr.write(f"Not enough instances of {model_class} found, need at least 1")
92
+ return
93
+
94
+ # Identify the placeholder for the uuid
95
+ replace_string = ""
96
+ if "<uuid:pk>" in url_pattern:
97
+ replace_string = "<uuid:pk>"
98
+ elif "(?P<pk>[/.]+)" in url_pattern:
99
+ replace_string = "(?P<pk>[/.]+)"
100
+
101
+ if replace_string:
102
+ # Replace the uuid with the actual uuid
103
+ if total_count == 1:
104
+ # Case where there is only one instance of the model
105
+ first_url_pattern = url_pattern.replace(replace_string, str(model_class.objects.first().pk))
106
+ if view_name not in self.app_name_to_urls["endpoints"]:
107
+ self.app_name_to_urls["endpoints"][view_name] = []
108
+ self.app_name_to_urls["endpoints"][view_name].append(first_url_pattern)
109
+ else:
110
+ # Case where there is more than one instance of the model
111
+ first_url_pattern = url_pattern.replace(replace_string, str(model_class.objects.first().pk))
112
+ second_url_pattern = url_pattern.replace(replace_string, str(model_class.objects.last().pk))
113
+ if view_name not in self.app_name_to_urls["endpoints"]:
114
+ self.app_name_to_urls["endpoints"][view_name] = []
115
+ self.app_name_to_urls["endpoints"][view_name].append(first_url_pattern)
116
+ self.app_name_to_urls["endpoints"][view_name].append(second_url_pattern)
117
+ # Handle list view url patterns
118
+ else:
119
+ if view_name not in self.app_name_to_urls["endpoints"]:
120
+ self.app_name_to_urls["endpoints"][view_name] = []
121
+ # One endpoint with default pagination
122
+ self.app_name_to_urls["endpoints"][view_name].append(url_pattern)
123
+ page_query_parameter = 5
124
+ per_page_query_parameter = total_count // page_query_parameter
125
+ if not is_api_endpoint:
126
+ query_params = urlencode(
127
+ {
128
+ "per_page": per_page_query_parameter,
129
+ "page": page_query_parameter,
130
+ }
131
+ )
132
+ else:
133
+ query_params = urlencode(
134
+ {
135
+ "limit": per_page_query_parameter,
136
+ "offset": per_page_query_parameter * (page_query_parameter - 1),
137
+ }
138
+ )
139
+ # One endpoint with non-default pagination
140
+ self.app_name_to_urls["endpoints"][view_name].append(url_pattern + f"?{query_params}")
141
+
142
+ def construct_view_name_and_url_pattern(self, pattern) -> tuple[Optional[str], Optional[str], bool]:
143
+ """
144
+ Args:
145
+ pattern (django.urls.resolvers.URLPattern): A URL pattern object.
146
+
147
+ Returns:
148
+ url_pattern (str): The URL pattern of the view.
149
+ view_name (str): The URL name of the view.
150
+ is_api_endpoint (bool): True if the endpoint is an API endpoint, False otherwise.
151
+ """
152
+ lookup_str_list = pattern.lookup_str.split(".")
153
+
154
+ # Determine if the endpoint belongs to a plugin
155
+ is_app = lookup_str_list[0] != "nautobot"
156
+ is_api_endpoint = "api" in lookup_str_list
157
+ # One of the nautobot apps: nautobot.circuits, nautobot.dcim, and etc. if not is_app
158
+ # One of the plugins: example_app, and etc. if is_app
159
+ app_name = lookup_str_list[0] if is_app else lookup_str_list[1]
160
+
161
+ model = pattern.default_args.get("model", None)
162
+ if model:
163
+ app_name = model._meta.app_label
164
+
165
+ if app_name == "users" and pattern.name in ["login", "logout"]:
166
+ # No need to test the login and logout endpoints for performance testing
167
+ url_pattern = f"/{pattern.pattern}" # /login, /logout
168
+ view_name = f"{pattern.name}" # login, logout
169
+ elif app_name == "core":
170
+ # Handle the special case where a view exist in the core app
171
+ # but its url pattern and view name does not include the prefix "/core" or "core:"
172
+ # ['nautobot', 'core', "views", "HomeView"]
173
+ # ['nautobot', 'core', "api", "views", "APIRootView"]
174
+ if pattern.name in ["api-root", "api-status", "graphql-api"]:
175
+ is_api_endpoint = True
176
+ url_pattern = f"/api/{pattern.pattern}" # /api/status
177
+ view_name = f"{pattern.name}" # api-status
178
+ elif pattern.name in ["home", "about", "search", "worker-status", "graphql", "metrics"]:
179
+ url_pattern = f"/{pattern.pattern}" # /home, /about, /search
180
+ view_name = f"{pattern.name}" # home, about, search
181
+ else:
182
+ url_pattern = None
183
+ view_name = None
184
+ elif app_name == "extras" and "plugins" in lookup_str_list:
185
+ # Handle the special case first for Installed apps related view is nested under the extras app.
186
+ # ['nautobot', 'extras', 'plugins', 'views', 'InstalledAppsView']
187
+
188
+ # We need special case handling to determine if the endpoint is an api endpoint as well for this view
189
+ view_class_name = lookup_str_list[-1]
190
+ if "API" in view_class_name:
191
+ is_api_endpoint = True
192
+ apps_or_plugins = "plugins" if "plugins" in pattern.name else "apps"
193
+ if is_api_endpoint:
194
+ url_pattern = f"/api/{apps_or_plugins}/{pattern.pattern}" # /api/apps/installed-apps
195
+ view_name = f"{apps_or_plugins}-api:{pattern.name}" # apps-api:apps-list
196
+ else:
197
+ url_pattern = f"/{apps_or_plugins}/{pattern.pattern}" # /apps/installed-apps
198
+ view_name = f"{apps_or_plugins}:{pattern.name}" # apps:apps_list
199
+ elif is_api_endpoint:
200
+ if not is_app:
201
+ # One of the nautobot apps: nautobot.circuits, nautobot.dcim, and etc.
202
+ url_pattern = f"/api/{app_name}/{pattern.pattern}" # /api/dcim/devices/
203
+ app_name = f"{app_name}-api" # dcim-api
204
+ view_name = f"{app_name}:{pattern.name}" # dcim-api:device-list
205
+ else:
206
+ api_app_name = f"{app_name}-api" # example_app-api
207
+ view_name = (
208
+ f"plugins-api:{api_app_name}:{pattern.name}" # plugins-api:example_app-api:examplemodel-list
209
+ )
210
+ app_name = app_name.replace("_", "-") # example_app -> example-app
211
+ url_pattern = f"/api/plugins/{app_name}/{pattern.pattern}" # /api/plugins/example-app/models/
212
+ else:
213
+ if not is_app:
214
+ url_pattern = f"/{app_name}/{pattern.pattern}" # /dcim/devices/
215
+ view_name = f"{app_name}:{pattern.name}" # dcim:device_list
216
+ else:
217
+ view_name = f"plugins:{app_name}:{pattern.name}" # plugins:example_app:examplemodel_list
218
+ app_name = app_name.replace("_", "-") # example_app -> example-app
219
+ url_pattern = f"/plugins/{app_name}/{pattern.pattern}" # /plugins/example-app/models/
220
+
221
+ return url_pattern, view_name, is_api_endpoint
222
+
223
+ def fetch_urls(self, url_patterns):
224
+ """
225
+ Store the URL patterns in the dictionary to output an .YAML file
226
+ The dictionary will have the following structure:
227
+ {
228
+ "endpoints": {
229
+ <app_name>:<view_name>: [
230
+ <url_pattern_1>,
231
+ <url_pattern_2>,
232
+ ],
233
+ dcim:device: [
234
+ "/dcim/devices/cfbd447f-d563-4fac-bb75-bdda70ab4e80/",
235
+ "/dcim/devices/38471bfe-0aca-4e09-b545-b0f90280fb66/",
236
+ ],
237
+ dcim-api:device-detail: [
238
+ "/api/dcim/devices/cfbd447f-d563-4fac-bb75-bdda70ab4e80/",
239
+ "/api/dcim/devices/38471bfe-0aca-4e09-b545-b0f90280fb66/",
240
+ ],
241
+ ...
242
+ },
243
+ ...
244
+ """
245
+ for pattern in url_patterns:
246
+ if hasattr(pattern, "url_patterns"):
247
+ # If it's a nested URL pattern, recursively list its URLs
248
+ self.fetch_urls(pattern.url_patterns)
249
+ else:
250
+ # Only fetch urls from relevant apps
251
+ if pattern.lookup_str.startswith(("nautobot.", *settings.PLUGINS)):
252
+ url_pattern, view_name, is_api_endpoint = self.construct_view_name_and_url_pattern(pattern)
253
+ # We do not need to test the ?format=<json,csv,api> endpoints and non-GET endpoints
254
+ if (
255
+ url_pattern is not None
256
+ and "(?P<format>[a-z0-9]+)" not in url_pattern
257
+ and "<drf_format_suffix:format>" not in url_pattern
258
+ and self.is_eligible_get_endpoint(view_name)
259
+ ):
260
+ # Replace "^" and "$" from the url pattern
261
+ url_pattern = url_pattern.replace("^", "").replace("$", "")
262
+ # Retrieve the model class for the view name
263
+ try:
264
+ model_class = get_model_for_view_name(view_name)
265
+ except ValueError:
266
+ # In case it is a generic view like /home, /about, /search
267
+ model_class = None
268
+ self.append_urls_to_dict(url_pattern, model_class, view_name, is_api_endpoint)
@@ -1,2 +1,2 @@
1
1
  {% extends 'generic/object_bulk_destroy.html' %}
2
- {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -7,7 +7,7 @@
7
7
  {% block content %}
8
8
  <div class="row">
9
9
  <div class="col-md-8 col-md-offset-2">
10
- <div class="panel panel-danger">
10
+ <div class="panel panel-danger" id="confirm-bulk-deletion">
11
11
  <div class="panel-heading"><strong>Confirm Bulk Deletion</strong></div>
12
12
  <div class="panel-body">
13
13
  <p><strong>Warning:</strong> The following operation will delete {{ total_objs_to_delete }} {{ obj_type_plural }}. {% if not delete_all %}Please carefully review the {{ obj_type_plural }} to be deleted and confirm below.{% endif %}</p>
@@ -1,2 +1,2 @@
1
1
  {% extends 'generic/object_bulk_update.html' %}
2
- {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -1,2 +1,2 @@
1
1
  {% extends 'generic/object_bulk_create.html' %}
2
- {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -64,3 +64,8 @@
64
64
  </div>
65
65
  </form>
66
66
  {% endblock %}
67
+
68
+ {% block javascript %}
69
+ {{ block.super }}
70
+ {{ form.media }}
71
+ {% endblock %}
@@ -1,2 +1,2 @@
1
1
  {% extends 'generic/object_destroy.html' %}
2
- {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -1,2 +1,2 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
- {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -1,2 +1,2 @@
1
1
  {% extends 'generic/object_create.html' %}
2
- {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}