nautobot 2.2.0b1__py3-none-any.whl → 2.2.1__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 (425) hide show
  1. nautobot/__init__.py +31 -0
  2. nautobot/apps/api.py +1 -2
  3. nautobot/apps/utils.py +4 -0
  4. nautobot/apps/views.py +2 -0
  5. nautobot/circuits/api/urls.py +1 -2
  6. nautobot/circuits/api/views.py +0 -12
  7. nautobot/circuits/apps.py +1 -1
  8. nautobot/circuits/tests/test_filters.py +1 -1
  9. nautobot/core/api/routers.py +50 -3
  10. nautobot/core/api/utils.py +4 -0
  11. nautobot/core/api/views.py +21 -15
  12. nautobot/core/cli/__init__.py +18 -11
  13. nautobot/core/constants.py +85 -0
  14. nautobot/core/filters.py +7 -1
  15. nautobot/core/forms/widgets.py +1 -2
  16. nautobot/core/graphql/schema.py +1 -0
  17. nautobot/core/management/commands/generate_test_data.py +4 -4
  18. nautobot/core/models/__init__.py +1 -0
  19. nautobot/core/settings.py +24 -3
  20. nautobot/core/settings.yaml +20 -0
  21. nautobot/core/signals.py +1 -0
  22. nautobot/core/tables.py +2 -1
  23. nautobot/core/templates/admin/base.html +23 -94
  24. nautobot/core/templates/generic/object_retrieve.html +2 -2
  25. nautobot/core/templates/graphene/graphiql.html +18 -47
  26. nautobot/core/templates/inc/footer.html +5 -5
  27. nautobot/core/templates/inc/javascript.html +4 -4
  28. nautobot/core/templates/inc/media.html +2 -2
  29. nautobot/core/templates/inc/nav_menu.html +0 -7
  30. nautobot/core/templates/nautobot_config.py.j2 +14 -1
  31. nautobot/core/templates/rest_framework/api.html +12 -5
  32. nautobot/core/templatetags/helpers.py +2 -2
  33. nautobot/core/testing/__init__.py +1 -1
  34. nautobot/core/testing/filters.py +1 -1
  35. nautobot/core/testing/views.py +30 -0
  36. nautobot/core/tests/integration/test_view_authentication.py +68 -0
  37. nautobot/core/tests/test_api.py +13 -6
  38. nautobot/core/tests/test_csv.py +5 -4
  39. nautobot/core/tests/test_filters.py +2 -1
  40. nautobot/core/tests/test_graphql.py +4 -14
  41. nautobot/core/tests/test_navigations.py +3 -0
  42. nautobot/core/tests/test_views.py +44 -16
  43. nautobot/core/utils/data.py +1 -2
  44. nautobot/core/utils/lookup.py +126 -0
  45. nautobot/core/views/__init__.py +3 -7
  46. nautobot/core/views/generic.py +20 -6
  47. nautobot/core/views/mixins.py +7 -1
  48. nautobot/core/views/renderers.py +11 -6
  49. nautobot/dcim/api/serializers.py +4 -4
  50. nautobot/dcim/api/urls.py +2 -3
  51. nautobot/dcim/api/views.py +7 -18
  52. nautobot/dcim/apps.py +8 -4
  53. nautobot/dcim/elevations.py +5 -1
  54. nautobot/dcim/factory.py +7 -7
  55. nautobot/dcim/filters/__init__.py +16 -17
  56. nautobot/dcim/forms.py +61 -45
  57. nautobot/dcim/homepage.py +11 -3
  58. nautobot/dcim/management/commands/migrate_location_contacts.py +218 -0
  59. nautobot/dcim/migrations/0057_controller_models.py +11 -70
  60. nautobot/dcim/models/__init__.py +2 -2
  61. nautobot/dcim/models/devices.py +14 -16
  62. nautobot/dcim/models/racks.py +1 -3
  63. nautobot/dcim/navigation.py +23 -31
  64. nautobot/dcim/signals.py +6 -6
  65. nautobot/dcim/tables/__init__.py +2 -2
  66. nautobot/dcim/tables/devices.py +13 -16
  67. nautobot/dcim/tables/template_code.py +1 -1
  68. nautobot/dcim/templates/dcim/controller_create.html +70 -0
  69. nautobot/dcim/templates/dcim/controller_retrieve.html +35 -18
  70. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +88 -0
  71. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +75 -43
  72. nautobot/dcim/templates/dcim/device.html +11 -3
  73. nautobot/dcim/templates/dcim/device_edit.html +1 -1
  74. nautobot/dcim/templates/dcim/devicefamily_retrieve.html +4 -0
  75. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +1 -1
  76. nautobot/dcim/tests/test_api.py +47 -6
  77. nautobot/dcim/tests/test_filters.py +92 -81
  78. nautobot/dcim/tests/test_graphql.py +11 -1
  79. nautobot/dcim/tests/test_models.py +15 -15
  80. nautobot/dcim/tests/test_signals.py +3 -1
  81. nautobot/dcim/tests/test_views.py +24 -12
  82. nautobot/dcim/urls.py +1 -1
  83. nautobot/dcim/views.py +25 -15
  84. nautobot/extras/api/serializers.py +20 -1
  85. nautobot/extras/api/urls.py +1 -2
  86. nautobot/extras/api/views.py +0 -10
  87. nautobot/extras/apps.py +7 -0
  88. nautobot/extras/context_managers.py +15 -4
  89. nautobot/extras/filters/__init__.py +53 -2
  90. nautobot/extras/filters/customfields.py +14 -9
  91. nautobot/extras/filters/mixins.py +6 -1
  92. nautobot/extras/forms/contacts.py +7 -0
  93. nautobot/extras/health_checks.py +1 -0
  94. nautobot/extras/jobs.py +1 -0
  95. nautobot/extras/managers.py +15 -2
  96. nautobot/extras/models/contacts.py +1 -0
  97. nautobot/extras/models/customfields.py +25 -2
  98. nautobot/extras/models/datasources.py +1 -0
  99. nautobot/extras/models/mixins.py +1 -0
  100. nautobot/extras/navigation.py +71 -65
  101. nautobot/extras/plugins/__init__.py +2 -1
  102. nautobot/extras/plugins/views.py +7 -11
  103. nautobot/extras/querysets.py +1 -2
  104. nautobot/extras/secrets/providers.py +1 -0
  105. nautobot/extras/signals.py +15 -5
  106. nautobot/extras/tasks.py +70 -17
  107. nautobot/extras/tests/test_api.py +2 -4
  108. nautobot/extras/tests/test_customfields.py +72 -9
  109. nautobot/extras/tests/test_dynamicgroups.py +2 -0
  110. nautobot/extras/tests/test_filters.py +89 -4
  111. nautobot/extras/tests/test_models.py +9 -0
  112. nautobot/extras/tests/test_relationships.py +10 -1
  113. nautobot/extras/tests/test_views.py +112 -1
  114. nautobot/extras/views.py +18 -17
  115. nautobot/ipam/api/serializers.py +10 -0
  116. nautobot/ipam/api/urls.py +1 -2
  117. nautobot/ipam/api/views.py +0 -11
  118. nautobot/ipam/apps.py +3 -2
  119. nautobot/ipam/tables.py +2 -22
  120. nautobot/ipam/tests/test_graphql.py +2 -3
  121. nautobot/ipam/tests/test_tables.py +42 -0
  122. nautobot/ipam/tests/test_views.py +1 -0
  123. nautobot/ipam/views.py +9 -9
  124. nautobot/project-static/css/base.css +1 -0
  125. nautobot/project-static/docs/404.html +126 -73
  126. nautobot/project-static/docs/apps/index.html +127 -71
  127. nautobot/project-static/docs/apps/nautobot-apps.html +127 -71
  128. nautobot/project-static/docs/assets/javascripts/{bundle.8fd75fb4.min.js → bundle.bd41221c.min.js} +2 -2
  129. nautobot/project-static/docs/assets/javascripts/{bundle.8fd75fb4.min.js.map → bundle.bd41221c.min.js.map} +3 -3
  130. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +1 -0
  131. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +1 -0
  132. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +127 -71
  133. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +127 -71
  134. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +167 -73
  135. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +165 -72
  136. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +127 -71
  137. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +127 -71
  138. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +127 -71
  139. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +127 -71
  140. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +127 -71
  141. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +127 -71
  142. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +127 -71
  143. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +127 -71
  144. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +127 -71
  145. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +127 -71
  146. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +127 -71
  147. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +127 -71
  148. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +127 -71
  149. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +127 -71
  150. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +128 -72
  151. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +127 -71
  152. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +127 -71
  153. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +345 -71
  154. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +172 -73
  155. nautobot/project-static/docs/development/apps/api/configuration-view.html +127 -71
  156. nautobot/project-static/docs/development/apps/api/database-backend-config.html +127 -71
  157. nautobot/project-static/docs/development/apps/api/models/django-admin.html +127 -71
  158. nautobot/project-static/docs/development/apps/api/models/global-search.html +127 -71
  159. nautobot/project-static/docs/development/apps/api/models/graphql.html +127 -71
  160. nautobot/project-static/docs/development/apps/api/models/index.html +127 -71
  161. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +127 -71
  162. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +127 -71
  163. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +127 -71
  164. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +127 -71
  165. nautobot/project-static/docs/development/apps/api/platform-features/index.html +127 -71
  166. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +127 -71
  167. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +127 -71
  168. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +127 -71
  169. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +127 -71
  170. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +127 -71
  171. nautobot/project-static/docs/development/apps/api/prometheus.html +127 -71
  172. nautobot/project-static/docs/development/apps/api/setup.html +127 -71
  173. nautobot/project-static/docs/development/apps/api/testing.html +127 -71
  174. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +127 -71
  175. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +127 -71
  176. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +127 -71
  177. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +127 -71
  178. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +127 -71
  179. nautobot/project-static/docs/development/apps/api/views/base-template.html +127 -71
  180. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +141 -80
  181. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +144 -83
  182. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +127 -71
  183. nautobot/project-static/docs/development/apps/api/views/index.html +127 -71
  184. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +127 -71
  185. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +127 -71
  186. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +127 -71
  187. nautobot/project-static/docs/development/apps/api/views/notes.html +127 -71
  188. nautobot/project-static/docs/development/apps/api/views/rest-api.html +127 -71
  189. nautobot/project-static/docs/development/apps/api/views/urls.html +127 -71
  190. nautobot/project-static/docs/development/apps/index.html +127 -71
  191. nautobot/project-static/docs/development/apps/migration/code-updates.html +127 -71
  192. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +127 -71
  193. nautobot/project-static/docs/development/apps/migration/from-v1.html +127 -71
  194. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +127 -71
  195. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +127 -71
  196. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +127 -71
  197. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +127 -71
  198. nautobot/project-static/docs/development/apps/porting-from-netbox.html +127 -71
  199. nautobot/project-static/docs/development/core/application-registry.html +127 -71
  200. nautobot/project-static/docs/development/core/best-practices.html +145 -79
  201. nautobot/project-static/docs/development/core/bootstrap-ui.html +127 -71
  202. nautobot/project-static/docs/development/core/caching.html +127 -71
  203. nautobot/project-static/docs/development/core/controllers.html +141 -275
  204. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +127 -71
  205. nautobot/project-static/docs/development/core/extending-models.html +13 -8166
  206. nautobot/project-static/docs/development/core/generic-views.html +142 -86
  207. nautobot/project-static/docs/development/core/getting-started.html +146 -81
  208. nautobot/project-static/docs/development/core/homepage.html +145 -89
  209. nautobot/project-static/docs/development/core/index.html +127 -71
  210. nautobot/project-static/docs/development/core/model-checklist.html +8354 -0
  211. nautobot/project-static/docs/development/core/model-features.html +130 -74
  212. nautobot/project-static/docs/development/core/natural-keys.html +127 -71
  213. nautobot/project-static/docs/development/core/navigation-menu.html +127 -71
  214. nautobot/project-static/docs/development/core/release-checklist.html +127 -71
  215. nautobot/project-static/docs/development/core/role-internals.html +127 -71
  216. nautobot/project-static/docs/development/core/settings.html +127 -71
  217. nautobot/project-static/docs/development/core/style-guide.html +127 -71
  218. nautobot/project-static/docs/development/core/templates.html +127 -71
  219. nautobot/project-static/docs/development/core/testing.html +127 -71
  220. nautobot/project-static/docs/development/core/user-preferences.html +127 -71
  221. nautobot/project-static/docs/development/extending-models.html +3 -3
  222. nautobot/project-static/docs/development/index.html +127 -71
  223. nautobot/project-static/docs/development/jobs/index.html +128 -72
  224. nautobot/project-static/docs/development/jobs/migration/from-v1.html +127 -71
  225. nautobot/project-static/docs/index.html +126 -73
  226. nautobot/project-static/docs/models/dcim/{controllerdevicegroup.html → controllermanageddevicegroup.html} +3 -3
  227. nautobot/project-static/docs/objects.inv +0 -0
  228. nautobot/project-static/docs/release-notes/index.html +127 -71
  229. nautobot/project-static/docs/release-notes/version-1.0.html +127 -71
  230. nautobot/project-static/docs/release-notes/version-1.1.html +127 -71
  231. nautobot/project-static/docs/release-notes/version-1.2.html +127 -71
  232. nautobot/project-static/docs/release-notes/version-1.3.html +127 -71
  233. nautobot/project-static/docs/release-notes/version-1.4.html +127 -71
  234. nautobot/project-static/docs/release-notes/version-1.5.html +127 -71
  235. nautobot/project-static/docs/release-notes/version-1.6.html +127 -71
  236. nautobot/project-static/docs/release-notes/version-2.0.html +127 -71
  237. nautobot/project-static/docs/release-notes/version-2.1.html +538 -254
  238. nautobot/project-static/docs/release-notes/version-2.2.html +520 -101
  239. nautobot/project-static/docs/requirements.txt +3 -3
  240. nautobot/project-static/docs/search/search_index.json +1 -1
  241. nautobot/project-static/docs/sitemap.xml +264 -259
  242. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  243. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +127 -71
  244. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +127 -71
  245. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +127 -71
  246. nautobot/project-static/docs/user-guide/administration/configuration/index.html +127 -71
  247. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +192 -71
  248. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +127 -71
  249. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +127 -71
  250. nautobot/project-static/docs/user-guide/administration/guides/caching.html +127 -71
  251. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +127 -71
  252. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +127 -71
  253. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +127 -71
  254. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +131 -71
  255. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +127 -71
  256. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +127 -71
  257. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +130 -74
  258. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +127 -71
  259. nautobot/project-static/docs/user-guide/administration/installation/docker.html +134 -74
  260. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +127 -71
  261. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +8616 -0
  262. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +127 -71
  263. nautobot/project-static/docs/user-guide/administration/installation/index.html +127 -71
  264. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +127 -71
  265. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +127 -71
  266. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +130 -74
  267. nautobot/project-static/docs/user-guide/administration/installation/services.html +127 -71
  268. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +127 -71
  269. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +127 -71
  270. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +127 -71
  271. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +127 -71
  272. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +127 -71
  273. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +127 -71
  274. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +127 -71
  275. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +127 -71
  276. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +127 -71
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +127 -71
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +127 -71
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +127 -71
  280. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +127 -71
  281. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +127 -71
  282. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +127 -71
  283. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +127 -71
  284. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +127 -71
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +127 -71
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +127 -71
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +127 -71
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +127 -71
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +127 -71
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +127 -71
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +362 -79
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/{controllerdevicegroup.html → controllermanageddevicegroup.html} +210 -85
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +127 -71
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +127 -71
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +127 -71
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +127 -71
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +127 -71
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +127 -71
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +127 -71
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +127 -71
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +127 -71
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +127 -71
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +127 -71
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +127 -71
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +127 -71
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +127 -71
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +127 -71
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +127 -71
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +127 -71
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +127 -71
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +127 -71
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +127 -71
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +127 -71
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +127 -71
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +127 -71
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +127 -71
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +127 -71
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +127 -71
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +127 -71
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +127 -71
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +127 -71
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +127 -71
  323. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +130 -74
  324. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +127 -71
  325. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +138 -71
  326. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +138 -71
  327. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +127 -71
  328. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +127 -71
  329. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +127 -71
  330. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +127 -71
  331. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +127 -71
  332. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +127 -71
  333. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +127 -71
  334. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +127 -71
  335. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +127 -71
  336. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +127 -71
  337. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +127 -71
  338. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +127 -71
  339. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +127 -71
  340. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +127 -71
  341. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +127 -71
  342. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +127 -71
  343. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +127 -71
  344. nautobot/project-static/docs/user-guide/feature-guides/{contact-and-team.html → contacts-and-teams.html} +128 -72
  345. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +129 -73
  346. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +127 -71
  347. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +127 -71
  348. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +127 -71
  349. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +127 -71
  350. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +127 -71
  351. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +127 -71
  352. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +129 -73
  353. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +127 -71
  354. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +127 -71
  355. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +127 -71
  356. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +127 -71
  357. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +127 -71
  358. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +127 -71
  359. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +127 -71
  360. nautobot/project-static/docs/user-guide/index.html +127 -71
  361. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +127 -71
  362. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +127 -71
  363. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +127 -71
  364. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +127 -71
  365. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +127 -71
  366. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +127 -71
  367. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +127 -71
  368. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +127 -71
  369. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +127 -71
  370. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +127 -71
  371. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +127 -71
  372. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +127 -71
  373. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +127 -71
  374. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +127 -71
  375. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +127 -71
  376. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +127 -71
  377. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +127 -71
  378. nautobot/project-static/docs/user-guide/platform-functionality/note.html +127 -71
  379. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +127 -71
  380. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +127 -71
  381. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +127 -71
  382. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +127 -71
  383. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +127 -71
  384. nautobot/project-static/docs/user-guide/platform-functionality/role.html +127 -71
  385. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +127 -71
  386. nautobot/project-static/docs/user-guide/platform-functionality/status.html +127 -71
  387. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +127 -71
  388. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +127 -71
  389. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +127 -71
  390. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +127 -71
  391. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +127 -71
  392. nautobot/project-static/jquery/jquery-3.7.1.min.js +2 -0
  393. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_444444_256x240.png +0 -0
  394. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_555555_256x240.png +0 -0
  395. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_777620_256x240.png +0 -0
  396. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_777777_256x240.png +0 -0
  397. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_cc0000_256x240.png +0 -0
  398. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_ffffff_256x240.png +0 -0
  399. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +7 -0
  400. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +6 -0
  401. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +5 -0
  402. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/jquery-ui.theme.min.css +1 -1
  403. nautobot/tenancy/api/urls.py +1 -2
  404. nautobot/tenancy/api/views.py +0 -12
  405. nautobot/tenancy/tables.py +1 -1
  406. nautobot/tenancy/tests/test_views.py +1 -0
  407. nautobot/users/api/urls.py +1 -2
  408. nautobot/users/api/views.py +2 -65
  409. nautobot/users/views.py +8 -8
  410. nautobot/virtualization/api/urls.py +1 -2
  411. nautobot/virtualization/api/views.py +0 -12
  412. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/METADATA +24 -24
  413. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/RECORD +418 -412
  414. nautobot/dcim/templates/dcim/controllerdevicegroup_create.html +0 -43
  415. nautobot/project-static/docs/assets/stylesheets/main.f2e4d321.min.css +0 -1
  416. nautobot/project-static/docs/assets/stylesheets/main.f2e4d321.min.css.map +0 -1
  417. nautobot/project-static/jquery/jquery-3.6.0.min.js +0 -2
  418. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.min.css +0 -7
  419. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.min.js +0 -6
  420. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.structure.min.css +0 -5
  421. /nautobot/dcim/templates/dcim/{controllerdevicegroup_retrieve.html → controllermanageddevicegroup_retrieve.html} +0 -0
  422. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/LICENSE.txt +0 -0
  423. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/NOTICE +0 -0
  424. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/WHEEL +0 -0
  425. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,218 @@
1
+ from textwrap import dedent, indent
2
+
3
+ from django.core.exceptions import ValidationError
4
+ from django.core.management.base import BaseCommand
5
+ from django.db import transaction
6
+
7
+ from nautobot.dcim.models import Location
8
+ from nautobot.extras.filters import ContactFilterSet, TeamFilterSet
9
+ from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Team
10
+
11
+
12
+ class Command(BaseCommand):
13
+ help = "Migrate Location contact fields to Contact and Team objects."
14
+ verbose_help = """
15
+ This command will present a series of prompts to guide you through migrating Locations that
16
+ have data in the `contact_name`, `contact_phone`, or `contact_email` fields which are not
17
+ already associated to a Contact or Team. This command will give you the option to create new
18
+ Contacts or Teams or, if a similar Contact or Team already exists, to link the Location to the
19
+ existing Contact or Team. Note that when assigning a Location to an existing Contact or Team
20
+ that has a blank `phone` or `email` field, the value from the Location will be copied to the
21
+ Contact/Team. After a Location has been associated to a Contact or Team, the `contact_name`,
22
+ `contact_phone`, and `contact_email` fields will be cleared from the Location.
23
+ """
24
+
25
+ def handle(self, *args, **kwargs):
26
+ status_role_err_msg = "No {0} found for the ContactAssociation content type. Please ensure {0} are created before running this command."
27
+ if not Status.objects.get_for_model(ContactAssociation).exists():
28
+ self.stdout.write(self.style.ERROR(status_role_err_msg.format("statuses")))
29
+ return
30
+ if not Role.objects.get_for_model(ContactAssociation).exists():
31
+ self.stdout.write(self.style.ERROR(status_role_err_msg.format("roles")))
32
+ return
33
+
34
+ self.stdout.write(self.style.NOTICE(" ".join(dedent(self.verbose_help).split("\n"))))
35
+
36
+ try:
37
+ with transaction.atomic():
38
+ try:
39
+ self.migrate_location_contacts()
40
+ except KeyboardInterrupt:
41
+ while True:
42
+ rollback = input("\nRoll back changes? [y/n]: ").strip().lower()
43
+ if rollback == "y":
44
+ raise
45
+ elif rollback == "n":
46
+ break
47
+ except KeyboardInterrupt:
48
+ self.stdout.write(self.style.ERROR("\nOperation cancelled, all changes rolled back."))
49
+ except:
50
+ self.stdout.write(self.style.ERROR("\nOperation failed, all changes rolled back."))
51
+ raise
52
+
53
+ def migrate_location_contacts(self):
54
+ """Iterate through Locations with contact information and try to match to existing Contact or Team."""
55
+ locations_with_contact_data = (
56
+ Location.objects.without_tree_fields()
57
+ .exclude(
58
+ contact_name="",
59
+ contact_phone="",
60
+ contact_email="",
61
+ )
62
+ .filter(associated_contacts__isnull=True)
63
+ )
64
+
65
+ for location in locations_with_contact_data:
66
+ self.stdout.write(f"Finding existing Contacts or Teams for location {location.display}...")
67
+ selected_contact = None
68
+ similar_contacts = list(ContactFilterSet(data={"similar_to_location_data": [location]}).qs)
69
+ similar_teams = list(TeamFilterSet(data={"similar_to_location_data": [location]}).qs)
70
+ similar_contacts_and_teams = similar_contacts + similar_teams
71
+
72
+ if not similar_contacts_and_teams:
73
+ self.stdout.write(
74
+ self.style.WARNING(f"No similar Contacts or Teams found for location {location.display}.")
75
+ )
76
+
77
+ else:
78
+ # Found similar contacts or teams, prompt user for action
79
+ self.stdout.write("")
80
+ self.stdout.write(self.style.WARNING(f"Found similar contacts/teams for location {location.display}:"))
81
+ self.stdout.write(f" current contact name: {location.contact_name!r}")
82
+ self.stdout.write(f" current contact phone: {location.contact_phone!r}")
83
+ self.stdout.write(f" current contact email: {location.contact_email!r}")
84
+ self.stdout.write("")
85
+
86
+ # Output menu of choices of valid contacts/teams
87
+ for i, contact in enumerate(similar_contacts_and_teams, start=1):
88
+ self.stdout.write(f"{self.style.WARNING(i)}: {contact._meta.model_name.title()}: {contact.name}")
89
+ self.print_contact_fields(contact, rjust=len(str(i)) + len(contact._meta.model_name) + 2)
90
+ self.stdout.write("")
91
+
92
+ self.stdout.write(self.style.WARNING("c") + ": Create a new Contact")
93
+ self.stdout.write(self.style.WARNING("t") + ": Create a new Team")
94
+ self.stdout.write(self.style.WARNING("s") + ": Skip this location")
95
+
96
+ # Retrieve desired contact/team from user input
97
+ while True:
98
+ choice = input("Select a choice from the list of items: ")
99
+ if choice == "s":
100
+ self.stdout.write(f"Skipping location {location.display}")
101
+ break
102
+ elif choice == "c":
103
+ selected_contact = self.create_new_contact_from_location(location, model=Contact)
104
+ break
105
+ elif choice == "t":
106
+ selected_contact = self.create_new_contact_from_location(location, model=Team)
107
+ break
108
+ elif choice.lower() == "q":
109
+ raise KeyboardInterrupt
110
+ elif choice.isdigit() and 0 < int(choice) <= len(similar_contacts_and_teams):
111
+ selected_contact = similar_contacts_and_teams[int(choice) - 1]
112
+ break
113
+
114
+ if selected_contact is not None:
115
+ self.associate_contact_to_location(selected_contact, location)
116
+
117
+ def associate_contact_to_location(self, contact, location):
118
+ role, status = self.prompt_for_role_and_status()
119
+
120
+ # If email or phone fields are present in the location but not the contact, update the contact fields
121
+ updated_fields = {}
122
+ if location.contact_phone and not contact.phone:
123
+ contact.phone = location.contact_phone
124
+ updated_fields["phone"] = location.contact_phone
125
+ if location.contact_email and not contact.email:
126
+ contact.email = location.contact_email
127
+ updated_fields["email"] = location.contact_email
128
+ if updated_fields:
129
+ try:
130
+ contact.validated_save()
131
+ self.stdout.write(
132
+ self.style.SUCCESS(
133
+ f"Updated {contact._meta.model_name.title()} {contact.name} field(s): {updated_fields!r}"
134
+ )
135
+ )
136
+ except ValidationError as e:
137
+ contact.refresh_from_db()
138
+ self.stdout.write(
139
+ self.style.ERROR(f"Attempted to update {contact!r} field(s) but failed: {updated_fields!r}: {e}")
140
+ )
141
+
142
+ try:
143
+ contact_association = ContactAssociation(
144
+ contact=contact if isinstance(contact, Contact) else None,
145
+ team=contact if isinstance(contact, Team) else None,
146
+ associated_object=location,
147
+ role=role,
148
+ status=status,
149
+ )
150
+ contact_association.validated_save()
151
+ location.contact_name = ""
152
+ location.contact_email = ""
153
+ location.contact_phone = ""
154
+ location.validated_save()
155
+ except Exception as e:
156
+ self.stdout.write(self.style.ERROR(f"Failed to create association: {e}"))
157
+ else:
158
+ self.stdout.write(self.style.SUCCESS(f"Associated {contact!r} to location {location.display}"))
159
+
160
+ def prompt_for_role_and_status(self):
161
+ # Prompt for role
162
+ self.stdout.write("\nValid roles for this association:")
163
+ valid_roles = list(Role.objects.get_for_model(ContactAssociation))
164
+ for i, role in enumerate(valid_roles, start=1):
165
+ self.stdout.write(self.style.WARNING(f"{i}") + f": {role}")
166
+ while True:
167
+ selected_role = input("Select a role for this association: ")
168
+ if selected_role.isdigit() and 0 < int(selected_role) <= len(valid_roles):
169
+ role = valid_roles[int(selected_role) - 1]
170
+ break
171
+
172
+ # Prompt for status
173
+ self.stdout.write("\nValid statuses for this association:")
174
+ valid_statuses = list(Status.objects.get_for_model(ContactAssociation))
175
+ for i, status in enumerate(valid_statuses, start=1):
176
+ self.stdout.write(self.style.WARNING(f"{i}") + f": {status}")
177
+ while True:
178
+ selected_status = input("Select a status for this association: ")
179
+ if selected_status.isdigit() and 0 < int(selected_status) <= len(valid_statuses):
180
+ status = valid_statuses[int(selected_status) - 1]
181
+ break
182
+
183
+ return role, status
184
+
185
+ def create_new_contact_from_location(self, location, model):
186
+ """Create a new Contact or Team from the location's contact data."""
187
+
188
+ self.stdout.write(f"Creating new {model._meta.model_name.title()} for location {location.display}...")
189
+ name = location.contact_name
190
+ phone = location.contact_phone
191
+ email = location.contact_email
192
+ self.stdout.write(f" contact name: {name!r}")
193
+ self.stdout.write(f" contact phone: {phone!r}")
194
+ self.stdout.write(f" contact email: {email!r}")
195
+
196
+ while not name:
197
+ name = input(f"Name is required. Enter a name for the new {model._meta.model_name.title()}: ")
198
+
199
+ try:
200
+ contact = model(
201
+ name=name,
202
+ phone=phone,
203
+ email=email,
204
+ )
205
+ contact.validated_save()
206
+ return contact
207
+ except Exception as e:
208
+ self.stdout.write(self.style.ERROR(f"Failed to create {model._meta.model_name.title()}: {e}"))
209
+ return None
210
+
211
+ def print_contact_fields(self, contact, rjust=0):
212
+ for field_name in ["phone", "email"]:
213
+ if getattr(contact, field_name):
214
+ self.stdout.write(
215
+ field_name.title().rjust(rjust)
216
+ + ": "
217
+ + indent(getattr(contact, field_name), " " * (rjust + 2)).lstrip()
218
+ )
@@ -1,4 +1,4 @@
1
- # Generated by Django 3.2.24 on 2024-03-08 10:18
1
+ # Generated by Django 3.2.24 on 2024-03-22 15:36
2
2
 
3
3
  import uuid
4
4
 
@@ -9,16 +9,13 @@ import django.db.models.deletion
9
9
  import nautobot.core.models.fields
10
10
  import nautobot.core.models.tree_queries
11
11
  import nautobot.extras.models.mixins
12
- import nautobot.extras.models.models
13
12
  import nautobot.extras.models.roles
14
13
  import nautobot.extras.models.statuses
15
- import nautobot.extras.utils
16
14
 
17
15
 
18
16
  class Migration(migrations.Migration):
19
17
  dependencies = [
20
18
  ("extras", "0106_populate_default_statuses_and_roles_for_contact_associations"),
21
- ("contenttypes", "0002_remove_content_type_name"),
22
19
  ("tenancy", "0009_update_all_charfields_max_length_to_255"),
23
20
  ("dcim", "0056_update_all_charfields_max_length_to_255"),
24
21
  ]
@@ -39,29 +36,24 @@ class Migration(migrations.Migration):
39
36
  "_custom_field_data",
40
37
  models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
41
38
  ),
42
- (
43
- "local_config_context_data",
44
- models.JSONField(blank=True, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True),
45
- ),
46
- ("local_config_context_data_owner_object_id", models.UUIDField(blank=True, default=None, null=True)),
47
39
  ("name", models.CharField(max_length=255, unique=True)),
48
40
  ("description", models.CharField(blank=True, max_length=255)),
49
41
  (
50
- "deployed_controller_device",
42
+ "controller_device",
51
43
  models.ForeignKey(
52
44
  blank=True,
53
45
  null=True,
54
- on_delete=django.db.models.deletion.SET_NULL,
46
+ on_delete=django.db.models.deletion.PROTECT,
55
47
  related_name="controllers",
56
48
  to="dcim.device",
57
49
  ),
58
50
  ),
59
51
  (
60
- "deployed_controller_group",
52
+ "controller_device_redundancy_group",
61
53
  models.ForeignKey(
62
54
  blank=True,
63
55
  null=True,
64
- on_delete=django.db.models.deletion.SET_NULL,
56
+ on_delete=django.db.models.deletion.PROTECT,
65
57
  related_name="controllers",
66
58
  to="dcim.deviceredundancygroup",
67
59
  ),
@@ -71,33 +63,11 @@ class Migration(migrations.Migration):
71
63
  models.ForeignKey(
72
64
  blank=True,
73
65
  null=True,
74
- on_delete=django.db.models.deletion.SET_NULL,
66
+ on_delete=django.db.models.deletion.PROTECT,
75
67
  related_name="controllers",
76
68
  to="extras.externalintegration",
77
69
  ),
78
70
  ),
79
- (
80
- "local_config_context_data_owner_content_type",
81
- nautobot.core.models.fields.ForeignKeyWithAutoRelatedName(
82
- blank=True,
83
- default=None,
84
- limit_choices_to=nautobot.extras.utils.FeatureQuery("config_context_owners"),
85
- null=True,
86
- on_delete=django.db.models.deletion.CASCADE,
87
- related_name="controllers",
88
- to="contenttypes.contenttype",
89
- ),
90
- ),
91
- (
92
- "local_config_context_schema",
93
- nautobot.core.models.fields.ForeignKeyWithAutoRelatedName(
94
- blank=True,
95
- null=True,
96
- on_delete=django.db.models.deletion.SET_NULL,
97
- related_name="controllers",
98
- to="extras.configcontextschema",
99
- ),
100
- ),
101
71
  (
102
72
  "location",
103
73
  models.ForeignKey(
@@ -149,11 +119,10 @@ class Migration(migrations.Migration):
149
119
  models.Model,
150
120
  nautobot.extras.models.mixins.DynamicGroupMixin,
151
121
  nautobot.extras.models.mixins.NotesMixin,
152
- nautobot.extras.models.models.ConfigContextSchemaValidationMixin,
153
122
  ),
154
123
  ),
155
124
  migrations.CreateModel(
156
- name="ControllerDeviceGroup",
125
+ name="ControllerManagedDeviceGroup",
157
126
  fields=[
158
127
  (
159
128
  "id",
@@ -167,43 +136,16 @@ class Migration(migrations.Migration):
167
136
  "_custom_field_data",
168
137
  models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
169
138
  ),
170
- (
171
- "local_config_context_data",
172
- models.JSONField(blank=True, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True),
173
- ),
174
- ("local_config_context_data_owner_object_id", models.UUIDField(blank=True, default=None, null=True)),
175
139
  ("name", models.CharField(max_length=255, unique=True)),
176
140
  ("weight", models.PositiveIntegerField(default=1000)),
177
141
  (
178
142
  "controller",
179
143
  models.ForeignKey(
180
144
  on_delete=django.db.models.deletion.CASCADE,
181
- related_name="controller_device_groups",
145
+ related_name="controller_managed_device_groups",
182
146
  to="dcim.controller",
183
147
  ),
184
148
  ),
185
- (
186
- "local_config_context_data_owner_content_type",
187
- nautobot.core.models.fields.ForeignKeyWithAutoRelatedName(
188
- blank=True,
189
- default=None,
190
- limit_choices_to=nautobot.extras.utils.FeatureQuery("config_context_owners"),
191
- null=True,
192
- on_delete=django.db.models.deletion.CASCADE,
193
- related_name="controller_device_groups",
194
- to="contenttypes.contenttype",
195
- ),
196
- ),
197
- (
198
- "local_config_context_schema",
199
- nautobot.core.models.fields.ForeignKeyWithAutoRelatedName(
200
- blank=True,
201
- null=True,
202
- on_delete=django.db.models.deletion.SET_NULL,
203
- related_name="controller_device_groups",
204
- to="extras.configcontextschema",
205
- ),
206
- ),
207
149
  (
208
150
  "parent",
209
151
  models.ForeignKey(
@@ -211,7 +153,7 @@ class Migration(migrations.Migration):
211
153
  null=True,
212
154
  on_delete=django.db.models.deletion.CASCADE,
213
155
  related_name="children",
214
- to="dcim.controllerdevicegroup",
156
+ to="dcim.controllermanageddevicegroup",
215
157
  ),
216
158
  ),
217
159
  ("tags", nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag")),
@@ -223,7 +165,6 @@ class Migration(migrations.Migration):
223
165
  models.Model,
224
166
  nautobot.extras.models.mixins.DynamicGroupMixin,
225
167
  nautobot.extras.models.mixins.NotesMixin,
226
- nautobot.extras.models.models.ConfigContextSchemaValidationMixin,
227
168
  ),
228
169
  managers=[
229
170
  ("objects", nautobot.core.models.tree_queries.TreeManager()),
@@ -231,13 +172,13 @@ class Migration(migrations.Migration):
231
172
  ),
232
173
  migrations.AddField(
233
174
  model_name="device",
234
- name="controller_device_group",
175
+ name="controller_managed_device_group",
235
176
  field=models.ForeignKey(
236
177
  blank=True,
237
178
  null=True,
238
179
  on_delete=django.db.models.deletion.SET_NULL,
239
180
  related_name="devices",
240
- to="dcim.controllerdevicegroup",
181
+ to="dcim.controllermanageddevicegroup",
241
182
  ),
242
183
  ),
243
184
  ]
@@ -27,7 +27,7 @@ from .device_components import (
27
27
  )
28
28
  from .devices import (
29
29
  Controller,
30
- ControllerDeviceGroup,
30
+ ControllerManagedDeviceGroup,
31
31
  Device,
32
32
  DeviceFamily,
33
33
  DeviceRedundancyGroup,
@@ -53,7 +53,7 @@ __all__ = (
53
53
  "ConsoleServerPort",
54
54
  "ConsoleServerPortTemplate",
55
55
  "Controller",
56
- "ControllerDeviceGroup",
56
+ "ControllerManagedDeviceGroup",
57
57
  "Device",
58
58
  "DeviceBay",
59
59
  "DeviceBayTemplate",
@@ -43,7 +43,7 @@ from .device_components import (
43
43
 
44
44
  __all__ = (
45
45
  "Controller",
46
- "ControllerDeviceGroup",
46
+ "ControllerManagedDeviceGroup",
47
47
  "Device",
48
48
  "DeviceRedundancyGroup",
49
49
  "DeviceType",
@@ -592,8 +592,8 @@ class Device(PrimaryModel, ConfigContextModel):
592
592
  verbose_name="Software Image Files",
593
593
  help_text="Override the software image files associated with the software version for this device",
594
594
  )
595
- controller_device_group = models.ForeignKey(
596
- to="dcim.ControllerDeviceGroup",
595
+ controller_managed_device_group = models.ForeignKey(
596
+ to="dcim.ControllerManagedDeviceGroup",
597
597
  on_delete=models.SET_NULL,
598
598
  related_name="devices",
599
599
  blank=True,
@@ -1247,7 +1247,7 @@ class SoftwareVersion(PrimaryModel):
1247
1247
  "statuses",
1248
1248
  "webhooks",
1249
1249
  )
1250
- class Controller(PrimaryModel, ConfigContextModel):
1250
+ class Controller(PrimaryModel):
1251
1251
  """Represents an entity that manages or controls one or more devices, acting as a central point of control.
1252
1252
 
1253
1253
  A Controller can be deployed to a single device or a group of devices represented by a DeviceRedundancyGroup.
@@ -1278,21 +1278,21 @@ class Controller(PrimaryModel, ConfigContextModel):
1278
1278
  )
1279
1279
  external_integration = models.ForeignKey(
1280
1280
  to="extras.ExternalIntegration",
1281
- on_delete=models.SET_NULL,
1281
+ on_delete=models.PROTECT,
1282
1282
  related_name="controllers",
1283
1283
  blank=True,
1284
1284
  null=True,
1285
1285
  )
1286
- deployed_controller_device = models.ForeignKey(
1286
+ controller_device = models.ForeignKey(
1287
1287
  to="dcim.Device",
1288
- on_delete=models.SET_NULL,
1288
+ on_delete=models.PROTECT,
1289
1289
  related_name="controllers",
1290
1290
  blank=True,
1291
1291
  null=True,
1292
1292
  )
1293
- deployed_controller_group = models.ForeignKey(
1293
+ controller_device_redundancy_group = models.ForeignKey(
1294
1294
  to="dcim.DeviceRedundancyGroup",
1295
- on_delete=models.SET_NULL,
1295
+ on_delete=models.PROTECT,
1296
1296
  related_name="controllers",
1297
1297
  blank=True,
1298
1298
  null=True,
@@ -1307,12 +1307,10 @@ class Controller(PrimaryModel, ConfigContextModel):
1307
1307
  def clean(self):
1308
1308
  super().clean()
1309
1309
 
1310
- if self.deployed_controller_device and self.deployed_controller_group:
1310
+ if self.controller_device and self.controller_device_redundancy_group:
1311
1311
  raise ValidationError(
1312
1312
  {
1313
- "deployed_controller_device": (
1314
- "Cannot assign both a device and a device redundancy group to a controller."
1315
- ),
1313
+ "controller_device": ("Cannot assign both a device and a device redundancy group to a controller."),
1316
1314
  },
1317
1315
  )
1318
1316
 
@@ -1331,7 +1329,7 @@ class Controller(PrimaryModel, ConfigContextModel):
1331
1329
  "graphql",
1332
1330
  "webhooks",
1333
1331
  )
1334
- class ControllerDeviceGroup(TreeModel, PrimaryModel, ConfigContextModel):
1332
+ class ControllerManagedDeviceGroup(TreeModel, PrimaryModel):
1335
1333
  """Represents a mapping of controlled devices to a specific controller.
1336
1334
 
1337
1335
  This model allows for the organization of controlled devices into hierarchical groups for structured representation.
@@ -1349,7 +1347,7 @@ class ControllerDeviceGroup(TreeModel, PrimaryModel, ConfigContextModel):
1349
1347
  controller = models.ForeignKey(
1350
1348
  to="dcim.Controller",
1351
1349
  on_delete=models.CASCADE,
1352
- related_name="controller_device_groups",
1350
+ related_name="controller_managed_device_groups",
1353
1351
  blank=False,
1354
1352
  null=False,
1355
1353
  help_text="Controller that manages the devices in this group",
@@ -1373,7 +1371,7 @@ class ControllerDeviceGroup(TreeModel, PrimaryModel, ConfigContextModel):
1373
1371
  if self.controller == self._original_controller and self.parent == self._original_parent:
1374
1372
  return
1375
1373
 
1376
- if self.parent and self.controller and self.parent.controller and self.controller != self.parent.controller:
1374
+ if self.parent and self.controller and self.controller != self.parent.controller:
1377
1375
  raise ValidationError(
1378
1376
  {"controller": "Controller device group must have the same controller as the parent group."}
1379
1377
  )
@@ -1,5 +1,3 @@
1
- from collections import OrderedDict
2
-
3
1
  from django.conf import settings
4
2
  from django.contrib.contenttypes.fields import GenericRelation
5
3
  from django.contrib.contenttypes.models import ContentType
@@ -275,7 +273,7 @@ class Rack(PrimaryModel):
275
273
  contains a height attribute for the device
276
274
  """
277
275
 
278
- elevation = OrderedDict()
276
+ elevation = {}
279
277
  for u in self.units:
280
278
  elevation[u] = {
281
279
  "id": u,
@@ -18,33 +18,33 @@ menu_items = (
18
18
  weight=150,
19
19
  items=(
20
20
  NavMenuItem(
21
- link="dcim:locationtype_list",
22
- name="Location Types",
21
+ link="dcim:location_list",
22
+ name="Locations",
23
23
  weight=100,
24
24
  permissions=[
25
- "dcim.view_locationtype",
25
+ "dcim.view_location",
26
26
  ],
27
27
  buttons=(
28
28
  NavMenuAddButton(
29
- link="dcim:locationtype_add",
29
+ link="dcim:location_add",
30
30
  permissions=[
31
- "dcim.add_locationtype",
31
+ "dcim.add_location",
32
32
  ],
33
33
  ),
34
34
  ),
35
35
  ),
36
36
  NavMenuItem(
37
- link="dcim:location_list",
38
- name="Locations",
37
+ link="dcim:locationtype_list",
38
+ name="Location Types",
39
39
  weight=200,
40
40
  permissions=[
41
- "dcim.view_location",
41
+ "dcim.view_locationtype",
42
42
  ],
43
43
  buttons=(
44
44
  NavMenuAddButton(
45
- link="dcim:location_add",
45
+ link="dcim:locationtype_add",
46
46
  permissions=[
47
- "dcim.add_location",
47
+ "dcim.add_locationtype",
48
48
  ],
49
49
  ),
50
50
  ),
@@ -211,33 +211,33 @@ menu_items = (
211
211
  ),
212
212
  ),
213
213
  NavMenuItem(
214
- link="dcim:manufacturer_list",
215
- name="Manufacturers",
214
+ link="dcim:devicefamily_list",
215
+ name="Device Families",
216
216
  weight=200,
217
217
  permissions=[
218
- "dcim.view_manufacturer",
218
+ "dcim.view_devicefamily",
219
219
  ],
220
220
  buttons=(
221
221
  NavMenuAddButton(
222
- link="dcim:manufacturer_add",
222
+ link="dcim:devicefamily_add",
223
223
  permissions=[
224
- "dcim.add_manufacturer",
224
+ "dcim.add_devicefamily",
225
225
  ],
226
226
  ),
227
227
  ),
228
228
  ),
229
229
  NavMenuItem(
230
- link="dcim:devicefamily_list",
231
- name="Device Families",
230
+ link="dcim:manufacturer_list",
231
+ name="Manufacturers",
232
232
  weight=300,
233
233
  permissions=[
234
- "dcim.view_devicefamily",
234
+ "dcim.view_manufacturer",
235
235
  ],
236
236
  buttons=(
237
237
  NavMenuAddButton(
238
- link="dcim:devicefamily_add",
238
+ link="dcim:manufacturer_add",
239
239
  permissions=[
240
- "dcim.add_devicefamily",
240
+ "dcim.add_manufacturer",
241
241
  ],
242
242
  ),
243
243
  ),
@@ -319,20 +319,12 @@ menu_items = (
319
319
  ),
320
320
  ),
321
321
  NavMenuItem(
322
- link="dcim:controllerdevicegroup_list",
323
- name="Controller Device Groups",
322
+ link="dcim:controllermanageddevicegroup_list",
323
+ name="Managed Device Groups",
324
324
  weight=200,
325
325
  permissions=[
326
- "dcim.view_controllerdevicegroup",
326
+ "dcim.view_controllermanageddevicegroup",
327
327
  ],
328
- buttons=(
329
- NavMenuAddButton(
330
- link="dcim:controllerdevicegroup_add",
331
- permissions=[
332
- "dcim.add_controllerdevicegroup",
333
- ],
334
- ),
335
- ),
336
328
  ),
337
329
  ),
338
330
  ),
nautobot/dcim/signals.py CHANGED
@@ -11,7 +11,7 @@ from nautobot.core.signals import disable_for_loaddata
11
11
  from .models import (
12
12
  Cable,
13
13
  CablePath,
14
- ControllerDeviceGroup,
14
+ ControllerManagedDeviceGroup,
15
15
  Device,
16
16
  DeviceRedundancyGroup,
17
17
  Interface,
@@ -290,17 +290,17 @@ def prevent_adding_tagged_vlans_with_incorrect_mode_or_site(sender, instance, ac
290
290
 
291
291
 
292
292
  #
293
- # ControllerDeviceGroup
293
+ # ControllerManagedDeviceGroup
294
294
  #
295
295
 
296
296
 
297
- @receiver(post_save, sender=ControllerDeviceGroup)
298
- def handle_controller_device_group_controller_change(instance, raw=False, **_):
299
- """Update descendants when the top level `ControllerDeviceGroup.controller` changes."""
297
+ @receiver(post_save, sender=ControllerManagedDeviceGroup)
298
+ def handle_controller_managed_device_group_controller_change(instance, raw=False, **_):
299
+ """Update descendants when the top level `ControllerManagedDeviceGroup.controller` changes."""
300
300
  if instance.parent or instance._original_controller == instance.controller:
301
301
  return
302
302
 
303
- logger = logging.getLogger(__name__ + ".ControllerDeviceGroup")
303
+ logger = logging.getLogger(__name__ + ".ControllerManagedDeviceGroup")
304
304
 
305
305
  if raw:
306
306
  logger.debug("Skipping controller update for imported controller device group %s", instance)