nautobot 2.2.9__py3-none-any.whl → 2.3.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (697) hide show
  1. nautobot/apps/forms.py +4 -0
  2. nautobot/apps/models.py +10 -1
  3. nautobot/circuits/__init__.py +0 -1
  4. nautobot/circuits/apps.py +1 -0
  5. nautobot/circuits/factory.py +15 -3
  6. nautobot/circuits/filters.py +13 -0
  7. nautobot/circuits/forms.py +13 -0
  8. nautobot/circuits/migrations/0021_alter_circuit_status_alter_circuittermination__path.py +32 -0
  9. nautobot/circuits/migrations/0022_circuittermination_cloud_network.py +25 -0
  10. nautobot/circuits/models.py +16 -3
  11. nautobot/circuits/tables.py +16 -2
  12. nautobot/circuits/templates/circuits/circuittermination_create.html +10 -2
  13. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +6 -0
  14. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -1
  15. nautobot/circuits/tests/test_api.py +7 -5
  16. nautobot/circuits/tests/test_filters.py +12 -5
  17. nautobot/circuits/tests/test_models.py +33 -2
  18. nautobot/circuits/views.py +2 -3
  19. nautobot/cloud/__init__.py +0 -0
  20. nautobot/cloud/api/__init__.py +0 -0
  21. nautobot/cloud/api/serializers.py +54 -0
  22. nautobot/cloud/api/urls.py +16 -0
  23. nautobot/cloud/api/views.py +48 -0
  24. nautobot/cloud/apps.py +13 -0
  25. nautobot/cloud/factory.py +111 -0
  26. nautobot/cloud/filters.py +184 -0
  27. nautobot/cloud/forms.py +333 -0
  28. nautobot/cloud/homepage.py +43 -0
  29. nautobot/cloud/migrations/0001_initial.py +304 -0
  30. nautobot/cloud/migrations/__init__.py +0 -0
  31. nautobot/cloud/models.py +247 -0
  32. nautobot/cloud/navigation.py +85 -0
  33. nautobot/cloud/tables.py +173 -0
  34. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
  35. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +128 -0
  36. nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
  37. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +100 -0
  38. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +65 -0
  39. nautobot/cloud/templates/cloud/cloudservice_update.html +25 -0
  40. nautobot/cloud/tests/__init__.py +0 -0
  41. nautobot/cloud/tests/test_api.py +248 -0
  42. nautobot/cloud/tests/test_filters.py +113 -0
  43. nautobot/cloud/tests/test_models.py +43 -0
  44. nautobot/cloud/tests/test_views.py +153 -0
  45. nautobot/cloud/urls.py +14 -0
  46. nautobot/cloud/views.py +181 -0
  47. nautobot/core/__init__.py +0 -3
  48. nautobot/core/api/metadata.py +1 -0
  49. nautobot/core/api/parsers.py +7 -1
  50. nautobot/core/api/urls.py +1 -0
  51. nautobot/core/api/utils.py +1 -0
  52. nautobot/core/api/views.py +4 -0
  53. nautobot/core/apps/__init__.py +6 -3
  54. nautobot/core/constants.py +8 -0
  55. nautobot/core/factory.py +32 -1
  56. nautobot/core/filters.py +96 -28
  57. nautobot/core/forms/fields.py +10 -4
  58. nautobot/core/forms/forms.py +1 -1
  59. nautobot/core/forms/widgets.py +18 -1
  60. nautobot/core/graphql/generators.py +2 -2
  61. nautobot/core/graphql/schema.py +34 -4
  62. nautobot/core/jobs/__init__.py +17 -6
  63. nautobot/core/jobs/cleanup.py +100 -0
  64. nautobot/core/jobs/groups.py +38 -0
  65. nautobot/core/management/commands/generate_test_data.py +116 -3
  66. nautobot/core/models/__init__.py +34 -9
  67. nautobot/core/models/generics.py +19 -3
  68. nautobot/core/models/name_color_content_types.py +7 -28
  69. nautobot/core/models/querysets.py +4 -3
  70. nautobot/core/models/tree_queries.py +1 -1
  71. nautobot/core/models/utils.py +21 -5
  72. nautobot/core/settings.py +4 -30
  73. nautobot/core/settings.yaml +34 -27
  74. nautobot/core/settings_funcs.py +103 -0
  75. nautobot/core/tables.py +127 -56
  76. nautobot/core/templates/admin/search_form.html +1 -1
  77. nautobot/core/templates/buttons/add.html +11 -3
  78. nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
  79. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
  80. nautobot/core/templates/buttons/export.html +101 -53
  81. nautobot/core/templates/buttons/job_import.html +11 -3
  82. nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
  83. nautobot/core/templates/generic/object_bulk_update.html +3 -1
  84. nautobot/core/templates/generic/object_changelog.html +0 -9
  85. nautobot/core/templates/generic/object_list.html +156 -17
  86. nautobot/core/templates/generic/object_retrieve.html +80 -16
  87. nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
  88. nautobot/core/templates/inc/javascript.html +2 -0
  89. nautobot/core/templates/inc/media.html +2 -2
  90. nautobot/core/templates/inc/nav_menu.html +1 -0
  91. nautobot/core/templates/inc/paginator.html +7 -7
  92. nautobot/core/templates/inc/search_panel.html +2 -2
  93. nautobot/core/templates/inc/table.html +2 -2
  94. nautobot/core/templates/nautobot_config.py.j2 +13 -23
  95. nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
  96. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
  97. nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
  98. nautobot/core/templates/utilities/theme_preview.html +25 -8
  99. nautobot/core/templates/utilities/worker_status.html +152 -0
  100. nautobot/core/templatetags/buttons.py +335 -38
  101. nautobot/core/templatetags/form_helpers.py +1 -1
  102. nautobot/core/templatetags/helpers.py +181 -11
  103. nautobot/core/testing/api.py +5 -4
  104. nautobot/core/testing/filters.py +51 -13
  105. nautobot/core/testing/mixins.py +46 -0
  106. nautobot/core/testing/models.py +22 -0
  107. nautobot/core/testing/schema.py +4 -8
  108. nautobot/core/testing/views.py +31 -14
  109. nautobot/core/tests/integration/test_general_functionality.py +1 -1
  110. nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
  111. nautobot/core/tests/integration/test_swagger.py +1 -1
  112. nautobot/core/tests/nautobot_config.py +0 -1
  113. nautobot/core/tests/runner.py +2 -2
  114. nautobot/core/tests/test_api.py +1 -0
  115. nautobot/core/tests/test_authentication.py +7 -2
  116. nautobot/core/tests/test_filters.py +11 -9
  117. nautobot/core/tests/test_forms.py +9 -0
  118. nautobot/core/tests/test_graphql.py +27 -16
  119. nautobot/core/tests/test_jobs.py +123 -74
  120. nautobot/core/tests/test_tables.py +3 -1
  121. nautobot/core/tests/test_templatetags_helpers.py +12 -5
  122. nautobot/core/tests/test_utils.py +31 -20
  123. nautobot/core/tests/test_views.py +6 -6
  124. nautobot/core/urls.py +8 -3
  125. nautobot/core/utils/deprecation.py +29 -0
  126. nautobot/core/utils/filtering.py +12 -9
  127. nautobot/core/utils/lookup.py +37 -2
  128. nautobot/core/utils/requests.py +4 -1
  129. nautobot/core/views/__init__.py +137 -24
  130. nautobot/core/views/generic.py +118 -66
  131. nautobot/core/views/mixins.py +104 -35
  132. nautobot/core/views/paginator.py +9 -3
  133. nautobot/core/views/renderers.py +121 -56
  134. nautobot/core/views/utils.py +79 -1
  135. nautobot/dcim/__init__.py +0 -1
  136. nautobot/dcim/api/serializers.py +180 -44
  137. nautobot/dcim/api/urls.py +7 -3
  138. nautobot/dcim/api/views.py +53 -7
  139. nautobot/dcim/apps.py +3 -0
  140. nautobot/dcim/choices.py +25 -0
  141. nautobot/dcim/constants.py +7 -0
  142. nautobot/dcim/factory.py +249 -18
  143. nautobot/dcim/filters/__init__.py +369 -193
  144. nautobot/dcim/filters/mixins.py +274 -1
  145. nautobot/dcim/forms.py +817 -109
  146. nautobot/dcim/graphql/types.py +2 -2
  147. nautobot/dcim/homepage.py +1 -1
  148. nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
  149. nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
  150. nautobot/dcim/migrations/0061_module_models.py +861 -0
  151. nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
  152. nautobot/dcim/models/__init__.py +8 -0
  153. nautobot/dcim/models/cables.py +15 -0
  154. nautobot/dcim/models/device_component_templates.py +207 -53
  155. nautobot/dcim/models/device_components.py +275 -106
  156. nautobot/dcim/models/devices.py +466 -13
  157. nautobot/dcim/navigation.py +47 -0
  158. nautobot/dcim/signals.py +3 -3
  159. nautobot/dcim/tables/__init__.py +35 -23
  160. nautobot/dcim/tables/devices.py +231 -59
  161. nautobot/dcim/tables/devicetypes.py +65 -9
  162. nautobot/dcim/tables/racks.py +5 -1
  163. nautobot/dcim/tables/template_code.py +46 -26
  164. nautobot/dcim/templates/dcim/cable_connect.html +76 -3
  165. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
  166. nautobot/dcim/templates/dcim/device/base.html +15 -7
  167. nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
  168. nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
  169. nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
  170. nautobot/dcim/templates/dcim/device/frontports.html +2 -3
  171. nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
  172. nautobot/dcim/templates/dcim/device/inventory.html +2 -3
  173. nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
  174. nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
  175. nautobot/dcim/templates/dcim/device/powerports.html +2 -3
  176. nautobot/dcim/templates/dcim/device/rearports.html +2 -3
  177. nautobot/dcim/templates/dcim/device.html +45 -1
  178. nautobot/dcim/templates/dcim/device_component.html +13 -5
  179. nautobot/dcim/templates/dcim/device_list.html +2 -1
  180. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -6
  181. nautobot/dcim/templates/dcim/devicetype.html +99 -98
  182. nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
  183. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  184. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
  185. nautobot/dcim/templates/dcim/interface.html +17 -2
  186. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
  187. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  188. nautobot/dcim/templates/dcim/manufacturer.html +24 -0
  189. nautobot/dcim/templates/dcim/module/base.html +97 -0
  190. nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
  191. nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
  192. nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
  193. nautobot/dcim/templates/dcim/module_destroy.html +5 -0
  194. nautobot/dcim/templates/dcim/module_frontports.html +53 -0
  195. nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
  196. nautobot/dcim/templates/dcim/module_list.html +20 -0
  197. nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
  198. nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
  199. nautobot/dcim/templates/dcim/module_powerports.html +53 -0
  200. nautobot/dcim/templates/dcim/module_rearports.html +53 -0
  201. nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
  202. nautobot/dcim/templates/dcim/module_update.html +71 -0
  203. nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
  204. nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
  205. nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
  206. nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
  207. nautobot/dcim/templates/dcim/moduletype_retrieve.html +142 -0
  208. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
  209. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
  210. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  211. nautobot/dcim/tests/test_api.py +691 -208
  212. nautobot/dcim/tests/test_filters.py +836 -217
  213. nautobot/dcim/tests/test_models.py +1072 -39
  214. nautobot/dcim/tests/test_views.py +1488 -358
  215. nautobot/dcim/urls.py +17 -2
  216. nautobot/dcim/utils.py +2 -3
  217. nautobot/dcim/views.py +1107 -120
  218. nautobot/extras/__init__.py +0 -1
  219. nautobot/extras/api/serializers.py +115 -3
  220. nautobot/extras/api/urls.py +12 -0
  221. nautobot/extras/api/views.py +125 -7
  222. nautobot/extras/apps.py +2 -2
  223. nautobot/extras/choices.py +43 -0
  224. nautobot/extras/context_managers.py +13 -8
  225. nautobot/extras/datasources/git.py +2 -0
  226. nautobot/extras/factory.py +422 -9
  227. nautobot/extras/filters/__init__.py +174 -3
  228. nautobot/extras/filters/mixins.py +46 -43
  229. nautobot/extras/forms/base.py +17 -4
  230. nautobot/extras/forms/forms.py +227 -8
  231. nautobot/extras/forms/mixins.py +93 -0
  232. nautobot/extras/graphql/types.py +23 -10
  233. nautobot/extras/homepage.py +16 -13
  234. nautobot/extras/jobs.py +2 -2
  235. nautobot/extras/management/__init__.py +1 -0
  236. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
  237. nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
  238. nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
  239. nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
  240. nautobot/extras/migrations/0111_metadata.py +162 -0
  241. nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
  242. nautobot/extras/migrations/0113_saved_views.py +77 -0
  243. nautobot/extras/models/__init__.py +15 -1
  244. nautobot/extras/models/change_logging.py +3 -3
  245. nautobot/extras/models/contacts.py +4 -0
  246. nautobot/extras/models/customfields.py +18 -3
  247. nautobot/extras/models/groups.py +389 -225
  248. nautobot/extras/models/jobs.py +4 -84
  249. nautobot/extras/models/metadata.py +441 -0
  250. nautobot/extras/models/mixins.py +72 -62
  251. nautobot/extras/models/models.py +116 -9
  252. nautobot/extras/models/relationships.py +9 -2
  253. nautobot/extras/models/tags.py +13 -2
  254. nautobot/extras/navigation.py +57 -0
  255. nautobot/extras/plugins/__init__.py +3 -1
  256. nautobot/extras/querysets.py +30 -66
  257. nautobot/extras/signals.py +96 -114
  258. nautobot/extras/tables.py +171 -47
  259. nautobot/extras/templates/extras/dynamicgroup.html +44 -15
  260. nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
  261. nautobot/extras/templates/extras/job.html +1 -1
  262. nautobot/extras/templates/extras/job_detail.html +0 -11
  263. nautobot/extras/templates/extras/jobresult.html +61 -74
  264. nautobot/extras/templates/extras/metadatatype_create.html +89 -0
  265. nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
  266. nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
  267. nautobot/extras/templates/extras/objectchange_list.html +0 -12
  268. nautobot/extras/templates/extras/plugins_list.html +1 -3
  269. nautobot/extras/templates/extras/role_retrieve.html +48 -0
  270. nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
  271. nautobot/extras/tests/integration/test_customfields.py +1 -0
  272. nautobot/extras/tests/test_api.py +501 -22
  273. nautobot/extras/tests/test_changelog.py +20 -9
  274. nautobot/extras/tests/test_context_managers.py +22 -15
  275. nautobot/extras/tests/test_datasources.py +13 -1
  276. nautobot/extras/tests/test_dynamicgroups.py +201 -171
  277. nautobot/extras/tests/test_filters.py +211 -12
  278. nautobot/extras/tests/test_jobs.py +4 -4
  279. nautobot/extras/tests/test_models.py +499 -4
  280. nautobot/extras/tests/test_relationships.py +1 -0
  281. nautobot/extras/tests/test_views.py +565 -28
  282. nautobot/extras/tests/test_webhooks.py +1 -1
  283. nautobot/extras/urls.py +5 -0
  284. nautobot/extras/utils.py +56 -45
  285. nautobot/extras/views.py +585 -96
  286. nautobot/ipam/__init__.py +0 -1
  287. nautobot/ipam/apps.py +1 -0
  288. nautobot/ipam/factory.py +17 -19
  289. nautobot/ipam/filters.py +14 -1
  290. nautobot/ipam/forms.py +9 -5
  291. nautobot/ipam/graphql/types.py +2 -2
  292. nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
  293. nautobot/ipam/models.py +23 -9
  294. nautobot/ipam/querysets.py +1 -1
  295. nautobot/ipam/signals.py +4 -2
  296. nautobot/ipam/tables.py +1 -0
  297. nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
  298. nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
  299. nautobot/ipam/templates/ipam/prefix.html +1 -0
  300. nautobot/ipam/tests/test_api.py +37 -18
  301. nautobot/ipam/tests/test_filters.py +26 -2
  302. nautobot/ipam/tests/test_models.py +8 -3
  303. nautobot/ipam/tests/test_querysets.py +1 -1
  304. nautobot/ipam/tests/test_views.py +3 -2
  305. nautobot/ipam/urls.py +2 -2
  306. nautobot/ipam/views.py +25 -28
  307. nautobot/project-static/css/base.css +20 -1
  308. nautobot/project-static/css/dark.css +11 -0
  309. nautobot/project-static/docs/404.html +884 -80
  310. nautobot/project-static/docs/apps/index.html +884 -80
  311. nautobot/project-static/docs/apps/nautobot-apps.html +884 -80
  312. nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
  313. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +911 -112
  314. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +896 -93
  315. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1457 -790
  316. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +927 -136
  317. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +969 -180
  318. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +893 -91
  319. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +889 -85
  320. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +983 -185
  321. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +938 -143
  322. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1064 -274
  323. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1190 -346
  324. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1663 -865
  325. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1156 -373
  326. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2200 -1502
  327. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2229 -1421
  328. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +904 -103
  329. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +955 -155
  330. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1002 -215
  331. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1911 -1275
  332. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1835 -1091
  333. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +896 -93
  334. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2323 -1693
  335. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1785 -1023
  336. nautobot/project-static/docs/development/apps/api/configuration-view.html +884 -80
  337. nautobot/project-static/docs/development/apps/api/database-backend-config.html +884 -80
  338. nautobot/project-static/docs/development/apps/api/models/django-admin.html +884 -80
  339. nautobot/project-static/docs/development/apps/api/models/global-search.html +884 -80
  340. nautobot/project-static/docs/development/apps/api/models/graphql.html +884 -80
  341. nautobot/project-static/docs/development/apps/api/models/index.html +922 -81
  342. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +884 -80
  343. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +884 -80
  344. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +884 -80
  345. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +884 -80
  346. nautobot/project-static/docs/development/apps/api/platform-features/index.html +884 -80
  347. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +884 -80
  348. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +884 -80
  349. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +884 -80
  350. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +884 -80
  351. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +884 -80
  352. nautobot/project-static/docs/development/apps/api/prometheus.html +884 -80
  353. nautobot/project-static/docs/development/apps/api/setup.html +884 -80
  354. nautobot/project-static/docs/development/apps/api/testing.html +884 -80
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +884 -80
  356. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +884 -80
  357. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +884 -80
  358. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +884 -80
  359. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +884 -80
  360. nautobot/project-static/docs/development/apps/api/views/base-template.html +884 -80
  361. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +884 -80
  362. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +884 -80
  363. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +884 -80
  364. nautobot/project-static/docs/development/apps/api/views/index.html +884 -80
  365. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +884 -80
  366. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +884 -80
  367. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +884 -80
  368. nautobot/project-static/docs/development/apps/api/views/notes.html +884 -80
  369. nautobot/project-static/docs/development/apps/api/views/rest-api.html +884 -80
  370. nautobot/project-static/docs/development/apps/api/views/urls.html +884 -80
  371. nautobot/project-static/docs/development/apps/index.html +884 -80
  372. nautobot/project-static/docs/development/apps/migration/code-updates.html +884 -80
  373. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +884 -80
  374. nautobot/project-static/docs/development/apps/migration/from-v1.html +884 -80
  375. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +884 -80
  376. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +884 -80
  377. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +884 -80
  378. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +884 -80
  379. nautobot/project-static/docs/development/apps/porting-from-netbox.html +884 -80
  380. nautobot/project-static/docs/development/core/application-registry.html +884 -80
  381. nautobot/project-static/docs/development/core/best-practices.html +885 -80
  382. nautobot/project-static/docs/development/core/bootstrap-ui.html +884 -80
  383. nautobot/project-static/docs/development/core/caching.html +884 -80
  384. nautobot/project-static/docs/development/core/controllers.html +884 -80
  385. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +884 -80
  386. nautobot/project-static/docs/development/core/generic-views.html +884 -80
  387. nautobot/project-static/docs/development/core/getting-started.html +884 -80
  388. nautobot/project-static/docs/development/core/homepage.html +884 -80
  389. nautobot/project-static/docs/development/core/index.html +884 -91
  390. nautobot/project-static/docs/development/core/model-checklist.html +887 -81
  391. nautobot/project-static/docs/development/core/model-features.html +884 -80
  392. nautobot/project-static/docs/development/core/natural-keys.html +884 -80
  393. nautobot/project-static/docs/development/core/navigation-menu.html +884 -80
  394. nautobot/project-static/docs/development/core/release-checklist.html +887 -83
  395. nautobot/project-static/docs/development/core/role-internals.html +884 -80
  396. nautobot/project-static/docs/development/core/settings.html +884 -80
  397. nautobot/project-static/docs/development/core/style-guide.html +885 -81
  398. nautobot/project-static/docs/development/core/templates.html +896 -81
  399. nautobot/project-static/docs/development/core/testing.html +884 -80
  400. nautobot/project-static/docs/development/core/user-preferences.html +884 -80
  401. nautobot/project-static/docs/development/index.html +884 -80
  402. nautobot/project-static/docs/development/jobs/index.html +1247 -457
  403. nautobot/project-static/docs/development/jobs/migration/from-v1.html +884 -80
  404. nautobot/project-static/docs/index.html +13 -8228
  405. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
  406. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
  407. nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
  408. nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
  409. nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
  410. nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
  411. nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
  412. nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
  413. nautobot/project-static/docs/models/dcim/module.html +15 -0
  414. nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
  415. nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
  416. nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
  417. nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
  418. nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
  419. nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
  420. nautobot/project-static/docs/models/extras/role.html +15 -0
  421. nautobot/project-static/docs/models/extras/savedview.html +15 -0
  422. nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
  423. nautobot/project-static/docs/models/extras/status.html +15 -0
  424. nautobot/project-static/docs/objects.inv +0 -0
  425. nautobot/project-static/docs/overview/application_stack.html +892 -81
  426. nautobot/project-static/docs/overview/design_philosophy.html +886 -82
  427. nautobot/project-static/docs/overview/index.html +9032 -13
  428. nautobot/project-static/docs/release-notes/index.html +887 -83
  429. nautobot/project-static/docs/release-notes/version-1.0.html +884 -80
  430. nautobot/project-static/docs/release-notes/version-1.1.html +884 -80
  431. nautobot/project-static/docs/release-notes/version-1.2.html +884 -80
  432. nautobot/project-static/docs/release-notes/version-1.3.html +884 -80
  433. nautobot/project-static/docs/release-notes/version-1.4.html +884 -80
  434. nautobot/project-static/docs/release-notes/version-1.5.html +885 -81
  435. nautobot/project-static/docs/release-notes/version-1.6.html +885 -81
  436. nautobot/project-static/docs/release-notes/version-2.0.html +884 -80
  437. nautobot/project-static/docs/release-notes/version-2.1.html +884 -80
  438. nautobot/project-static/docs/release-notes/version-2.2.html +990 -323
  439. nautobot/project-static/docs/release-notes/version-2.3.html +9524 -0
  440. nautobot/project-static/docs/requirements.txt +4 -4
  441. nautobot/project-static/docs/search/search_index.json +1 -1
  442. nautobot/project-static/docs/sitemap.xml +335 -260
  443. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  444. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +884 -80
  445. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +884 -80
  446. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +884 -80
  447. nautobot/project-static/docs/user-guide/administration/configuration/index.html +884 -80
  448. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +983 -197
  449. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +884 -80
  450. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +884 -80
  451. nautobot/project-static/docs/user-guide/administration/guides/caching.html +884 -80
  452. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +888 -84
  453. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +884 -80
  454. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +884 -80
  455. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +884 -80
  456. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +884 -80
  457. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +884 -80
  458. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +884 -80
  459. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +884 -80
  460. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +884 -80
  461. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +884 -80
  462. nautobot/project-static/docs/user-guide/administration/installation/index.html +888 -80
  463. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +884 -80
  464. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +884 -80
  465. nautobot/project-static/docs/user-guide/administration/installation/services.html +888 -80
  466. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +900 -91
  467. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +884 -80
  468. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +884 -80
  469. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +884 -80
  470. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +884 -80
  471. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +915 -163
  472. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +884 -80
  473. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +884 -80
  474. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +884 -80
  475. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +884 -80
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +884 -80
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +884 -80
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +884 -80
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +884 -80
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +884 -80
  481. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +884 -80
  482. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +884 -80
  483. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +885 -81
  484. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +884 -80
  485. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +888 -80
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +887 -83
  487. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
  488. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
  489. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
  494. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +898 -94
  495. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +915 -97
  496. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +915 -97
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +910 -92
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +915 -97
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +898 -94
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +898 -94
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +905 -97
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +912 -108
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +913 -109
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +910 -106
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +898 -94
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +906 -97
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +918 -100
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +928 -110
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +920 -98
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +898 -94
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +929 -111
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +920 -102
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +910 -106
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +913 -109
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +914 -106
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +908 -104
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +898 -94
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +932 -75
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +916 -98
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +898 -94
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +935 -78
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +913 -95
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +921 -117
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +910 -106
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +898 -94
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +914 -96
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +916 -98
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +898 -94
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +898 -94
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +898 -94
  535. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +884 -80
  536. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +884 -80
  537. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +889 -81
  538. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +889 -81
  539. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +884 -80
  540. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +884 -80
  541. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +884 -80
  542. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +884 -80
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +884 -80
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +884 -80
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +884 -80
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +884 -80
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +884 -80
  548. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +884 -80
  549. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +893 -88
  550. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +884 -80
  551. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +884 -80
  552. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +884 -80
  553. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +884 -80
  554. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +884 -80
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +889 -81
  556. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +884 -80
  557. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +884 -80
  558. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +884 -80
  559. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +884 -80
  560. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +884 -80
  561. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +884 -80
  562. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +884 -80
  563. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +884 -80
  564. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +884 -80
  565. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +884 -80
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +884 -80
  567. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +884 -80
  568. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +884 -80
  569. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
  570. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
  571. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
  572. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
  573. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
  574. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
  575. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
  576. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
  587. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
  589. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
  590. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
  591. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
  592. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
  593. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
  594. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +884 -80
  595. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +884 -80
  596. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +884 -80
  597. nautobot/project-static/docs/user-guide/index.html +884 -80
  598. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +884 -80
  599. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +884 -80
  600. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +884 -80
  601. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +884 -80
  602. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1250 -777
  603. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +887 -83
  604. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +884 -80
  605. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +884 -80
  606. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +884 -80
  607. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +884 -80
  608. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +884 -80
  609. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +884 -80
  610. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +887 -83
  611. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +884 -80
  612. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +884 -80
  613. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +888 -80
  614. nautobot/project-static/docs/user-guide/platform-functionality/metadata.html +8948 -0
  615. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +887 -83
  616. nautobot/project-static/docs/user-guide/platform-functionality/note.html +884 -80
  617. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +884 -80
  618. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +884 -80
  619. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +884 -80
  620. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +884 -80
  621. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +884 -80
  622. nautobot/project-static/docs/user-guide/platform-functionality/role.html +887 -83
  623. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
  624. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +887 -83
  625. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
  626. nautobot/project-static/docs/user-guide/platform-functionality/status.html +884 -80
  627. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +884 -80
  628. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +942 -113
  629. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +884 -80
  630. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +884 -80
  631. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +884 -80
  632. nautobot/project-static/js/forms.js +71 -0
  633. nautobot/project-static/js/table_sorting_indicator.js +46 -0
  634. nautobot/project-static/js/tableconfig.js +6 -1
  635. nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
  636. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
  637. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
  638. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
  639. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
  640. nautobot/tenancy/__init__.py +0 -1
  641. nautobot/tenancy/apps.py +1 -0
  642. nautobot/tenancy/factory.py +3 -2
  643. nautobot/tenancy/filters/__init__.py +1 -0
  644. nautobot/tenancy/forms.py +1 -1
  645. nautobot/tenancy/templates/tenancy/tenant.html +22 -18
  646. nautobot/tenancy/views.py +11 -10
  647. nautobot/users/__init__.py +0 -1
  648. nautobot/users/api/serializers.py +1 -1
  649. nautobot/users/api/views.py +4 -2
  650. nautobot/users/apps.py +3 -2
  651. nautobot/users/factory.py +3 -3
  652. nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
  653. nautobot/users/models.py +12 -0
  654. nautobot/users/tests/test_filters.py +6 -3
  655. nautobot/users/urls.py +8 -0
  656. nautobot/virtualization/__init__.py +0 -1
  657. nautobot/virtualization/apps.py +1 -0
  658. nautobot/virtualization/filters.py +6 -1
  659. nautobot/virtualization/forms.py +11 -3
  660. nautobot/virtualization/graphql/types.py +2 -2
  661. nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
  662. nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
  663. nautobot/virtualization/tables.py +15 -5
  664. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  665. nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
  666. nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
  667. nautobot/virtualization/tests/test_api.py +9 -4
  668. nautobot/virtualization/tests/test_filters.py +22 -0
  669. nautobot/virtualization/tests/test_models.py +7 -3
  670. nautobot/virtualization/tests/test_views.py +19 -3
  671. nautobot/virtualization/urls.py +2 -2
  672. nautobot/virtualization/views.py +10 -32
  673. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/METADATA +21 -19
  674. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/RECORD +679 -559
  675. nautobot/project-static/materialdesignicons-6.5.95/.github/ISSUE_TEMPLATE.md +0 -3
  676. nautobot/project-static/materialdesignicons-6.5.95/README.md +0 -25
  677. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css +0 -26654
  678. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css.map +0 -16
  679. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css +0 -3
  680. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css.map +0 -16
  681. nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff +0 -0
  682. nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff2 +0 -0
  683. nautobot/project-static/materialdesignicons-6.5.95/package.json +0 -28
  684. nautobot/project-static/materialdesignicons-6.5.95/preview.html +0 -717
  685. nautobot/project-static/materialdesignicons-6.5.95/scss/_animated.scss +0 -27
  686. nautobot/project-static/materialdesignicons-6.5.95/scss/_core.scss +0 -10
  687. nautobot/project-static/materialdesignicons-6.5.95/scss/_extras.scss +0 -65
  688. nautobot/project-static/materialdesignicons-6.5.95/scss/_functions.scss +0 -20
  689. nautobot/project-static/materialdesignicons-6.5.95/scss/_icons.scss +0 -10
  690. nautobot/project-static/materialdesignicons-6.5.95/scss/_path.scss +0 -10
  691. nautobot/project-static/materialdesignicons-6.5.95/scss/_variables.scss +0 -6606
  692. nautobot/project-static/materialdesignicons-6.5.95/scss/materialdesignicons.scss +0 -8
  693. /nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/LICENSE +0 -0
  694. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/LICENSE.txt +0 -0
  695. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/NOTICE +0 -0
  696. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/WHEEL +0 -0
  697. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/entry_points.txt +0 -0
@@ -24,6 +24,7 @@ from nautobot.dcim.choices import (
24
24
  SoftwareImageFileHashingAlgorithmChoices,
25
25
  SubdeviceRoleChoices,
26
26
  )
27
+ from nautobot.dcim.constants import MODULE_RECURSION_DEPTH_LIMIT
27
28
  from nautobot.dcim.utils import get_all_network_driver_mappings
28
29
  from nautobot.extras.models import ChangeLoggedModel, ConfigContextModel, RoleField, StatusField
29
30
  from nautobot.extras.querysets import ConfigContextModelQuerySet
@@ -36,6 +37,7 @@ from .device_components import (
36
37
  FrontPort,
37
38
  Interface,
38
39
  InventoryItem,
40
+ ModuleBay,
39
41
  PowerOutlet,
40
42
  PowerPort,
41
43
  RearPort,
@@ -110,6 +112,7 @@ class DeviceTypeToSoftwareImageFile(BaseModel, ChangeLoggedModel):
110
112
  software_image_file = models.ForeignKey(
111
113
  "dcim.SoftwareImageFile", on_delete=models.PROTECT, related_name="device_type_mappings"
112
114
  )
115
+ is_metadata_associable_model = False
113
116
 
114
117
  class Meta:
115
118
  unique_together = [
@@ -296,6 +299,16 @@ class DeviceType(PrimaryModel):
296
299
  }
297
300
  for c in self.device_bay_templates.all()
298
301
  ]
302
+ if self.module_bay_templates.exists():
303
+ data["module-bays"] = [
304
+ {
305
+ "name": c.name,
306
+ "position": c.position,
307
+ "label": c.label,
308
+ "description": c.description,
309
+ }
310
+ for c in self.module_bay_templates.all()
311
+ ]
299
312
 
300
313
  return yaml.dump(dict(data), sort_keys=False, allow_unicode=True)
301
314
 
@@ -733,7 +746,7 @@ class Device(PrimaryModel, ConfigContextModel):
733
746
  pass
734
747
 
735
748
  # Validate primary IP addresses
736
- vc_interfaces = self.vc_interfaces.all()
749
+ all_interfaces = self.all_interfaces.all()
737
750
  for field in ["primary_ip4", "primary_ip6"]:
738
751
  ip = getattr(self, field)
739
752
  if ip is not None:
@@ -743,12 +756,14 @@ class Device(PrimaryModel, ConfigContextModel):
743
756
  else:
744
757
  if ip.ip_version != 6:
745
758
  raise ValidationError({f"{field}": f"{ip} is not an IPv6 address."})
746
- if ipam_models.IPAddressToInterface.objects.filter(ip_address=ip, interface__in=vc_interfaces).exists():
759
+ if ipam_models.IPAddressToInterface.objects.filter(
760
+ ip_address=ip, interface__in=all_interfaces
761
+ ).exists():
747
762
  pass
748
763
  elif (
749
764
  ip.nat_inside is not None
750
765
  and ipam_models.IPAddressToInterface.objects.filter(
751
- ip_address=ip.nat_inside, interface__in=vc_interfaces
766
+ ip_address=ip.nat_inside, interface__in=all_interfaces
752
767
  ).exists()
753
768
  ):
754
769
  pass
@@ -847,7 +862,7 @@ class Device(PrimaryModel, ConfigContextModel):
847
862
  """Create device components from the device type definition."""
848
863
  # The order of these is significant as
849
864
  # - PowerOutlet depends on PowerPort
850
- # - FrontPort depends on FrontPort
865
+ # - FrontPort depends on RearPort
851
866
  component_models = [
852
867
  (ConsolePort, self.device_type.console_port_templates.all()),
853
868
  (ConsoleServerPort, self.device_type.console_server_port_templates.all()),
@@ -857,10 +872,11 @@ class Device(PrimaryModel, ConfigContextModel):
857
872
  (RearPort, self.device_type.rear_port_templates.all()),
858
873
  (FrontPort, self.device_type.front_port_templates.all()),
859
874
  (DeviceBay, self.device_type.device_bay_templates.all()),
875
+ (ModuleBay, self.device_type.module_bay_templates.all()),
860
876
  ]
861
877
  instantiated_components = []
862
878
  for model, templates in component_models:
863
- model.objects.bulk_create([x.instantiate(self) for x in templates])
879
+ model.objects.bulk_create([x.instantiate(device=self) for x in templates])
864
880
  return instantiated_components
865
881
 
866
882
  @property
@@ -906,10 +922,11 @@ class Device(PrimaryModel, ConfigContextModel):
906
922
  Return a QuerySet matching all Interfaces assigned to this Device or, if this Device is a VC master, to another
907
923
  Device belonging to the same VirtualChassis.
908
924
  """
909
- filter_q = Q(device=self)
925
+ qs = self.all_interfaces
910
926
  if self.virtual_chassis and self.virtual_chassis.master == self:
911
- filter_q |= Q(device__virtual_chassis=self.virtual_chassis, mgmt_only=False)
912
- return Interface.objects.filter(filter_q)
927
+ for member in self.virtual_chassis.members.exclude(id=self.id):
928
+ qs |= member.all_interfaces.filter(mgmt_only=False)
929
+ return qs
913
930
 
914
931
  @property
915
932
  def common_vc_interfaces(self):
@@ -919,7 +936,7 @@ class Device(PrimaryModel, ConfigContextModel):
919
936
  """
920
937
  if self.virtual_chassis:
921
938
  return self.virtual_chassis.member_interfaces
922
- return self.interfaces
939
+ return self.all_interfaces
923
940
 
924
941
  def get_cables(self, pk_list=False):
925
942
  """
@@ -950,6 +967,79 @@ class Device(PrimaryModel, ConfigContextModel):
950
967
  """
951
968
  return Device.objects.filter(parent_bay__device=self.pk)
952
969
 
970
+ @property
971
+ def all_modules(self):
972
+ """
973
+ Return all child Modules installed in ModuleBays within this Device.
974
+ """
975
+ # Supports Device->ModuleBay->Module->ModuleBay->Module->ModuleBay->Module->ModuleBay->Module
976
+ # This query looks for modules that are installed in a module_bay and attached to this device
977
+ # We artificially limit the recursion to 4 levels or we would be stuck in an infinite loop.
978
+ recursion_depth = MODULE_RECURSION_DEPTH_LIMIT
979
+ qs = Module.objects.all()
980
+ query = Q()
981
+ for level in range(recursion_depth):
982
+ recursive_query = "parent_module_bay__parent_module__" * level
983
+ query = query | Q(**{f"{recursive_query}parent_module_bay__parent_device": self})
984
+ return qs.filter(query)
985
+
986
+ @property
987
+ def all_console_ports(self):
988
+ """
989
+ Return all Console Ports that are installed in the device or in modules that are installed in the device.
990
+ """
991
+ # TODO: These could probably be optimized to reduce the number of joins
992
+ return ConsolePort.objects.filter(Q(device=self) | Q(module__in=self.all_modules))
993
+
994
+ @property
995
+ def all_console_server_ports(self):
996
+ """
997
+ Return all Console Server Ports that are installed in the device or in modules that are installed in the device.
998
+ """
999
+ return ConsoleServerPort.objects.filter(Q(device=self) | Q(module__in=self.all_modules))
1000
+
1001
+ @property
1002
+ def all_front_ports(self):
1003
+ """
1004
+ Return all Front Ports that are installed in the device or in modules that are installed in the device.
1005
+ """
1006
+ return FrontPort.objects.filter(Q(device=self) | Q(module__in=self.all_modules))
1007
+
1008
+ @property
1009
+ def all_interfaces(self):
1010
+ """
1011
+ Return all Interfaces that are installed in the device or in modules that are installed in the device.
1012
+ """
1013
+ return Interface.objects.filter(Q(device=self) | Q(module__in=self.all_modules))
1014
+
1015
+ @property
1016
+ def all_module_bays(self):
1017
+ """
1018
+ Return all Module Bays that are installed in the device or in modules that are installed in the device.
1019
+ """
1020
+ return ModuleBay.objects.filter(Q(parent_device=self) | Q(parent_module__in=self.all_modules))
1021
+
1022
+ @property
1023
+ def all_power_ports(self):
1024
+ """
1025
+ Return all Power Ports that are installed in the device or in modules that are installed in the device.
1026
+ """
1027
+ return PowerPort.objects.filter(Q(device=self) | Q(module__in=self.all_modules))
1028
+
1029
+ @property
1030
+ def all_power_outlets(self):
1031
+ """
1032
+ Return all Power Outlets that are installed in the device or in modules that are installed in the device.
1033
+ """
1034
+ return PowerOutlet.objects.filter(Q(device=self) | Q(module__in=self.all_modules))
1035
+
1036
+ @property
1037
+ def all_rear_ports(self):
1038
+ """
1039
+ Return all Rear Ports that are installed in the device or in modules that are installed in the device.
1040
+ """
1041
+ return RearPort.objects.filter(Q(device=self) | Q(module__in=self.all_modules))
1042
+
953
1043
 
954
1044
  #
955
1045
  # Virtual chassis
@@ -1065,10 +1155,6 @@ class DeviceRedundancyGroup(PrimaryModel):
1065
1155
  def devices_sorted(self):
1066
1156
  return self.devices.order_by("device_redundancy_group_priority")
1067
1157
 
1068
- @property
1069
- def controllers_sorted(self):
1070
- return self.controllers.order_by("name")
1071
-
1072
1158
  def __str__(self):
1073
1159
  return self.name
1074
1160
 
@@ -1386,3 +1472,370 @@ class ControllerManagedDeviceGroup(TreeModel, PrimaryModel):
1386
1472
  raise ValidationError(
1387
1473
  {"controller": "Controller device group must have the same controller as the parent group."}
1388
1474
  )
1475
+
1476
+
1477
+ #
1478
+ # Modules
1479
+ #
1480
+
1481
+
1482
+ # TODO: 5840 - Translate comments field from devicetype library, Nautobot doesn't use that field for ModuleType
1483
+ @extras_features(
1484
+ "custom_links",
1485
+ "custom_validators",
1486
+ "export_templates",
1487
+ "graphql",
1488
+ "webhooks",
1489
+ )
1490
+ class ModuleType(PrimaryModel):
1491
+ """
1492
+ A ModuleType represents a particular make (Manufacturer) and model of Module. A Module can represent
1493
+ a line card, supervisor, or other interchangeable hardware component within a ModuleBay.
1494
+
1495
+ ModuleType implements a subset of the features of DeviceType.
1496
+
1497
+ Each ModuleType can have an arbitrary number of component templates assigned to it,
1498
+ which define console, power, and interface objects. For example, a Cisco WS-SUP720-3B
1499
+ ModuleType would have:
1500
+
1501
+ * 1 ConsolePortTemplate
1502
+ * 2 InterfaceTemplates
1503
+
1504
+ When a new Module of this type is created, the appropriate console, power, and interface
1505
+ objects (as defined by the ModuleType) are automatically created as well.
1506
+ """
1507
+
1508
+ manufacturer = models.ForeignKey(to="dcim.Manufacturer", on_delete=models.PROTECT, related_name="module_types")
1509
+ model = models.CharField(max_length=CHARFIELD_MAX_LENGTH)
1510
+ part_number = models.CharField(
1511
+ max_length=CHARFIELD_MAX_LENGTH, blank=True, help_text="Discrete part number (optional)"
1512
+ )
1513
+
1514
+ clone_fields = [
1515
+ "manufacturer",
1516
+ ]
1517
+
1518
+ class Meta:
1519
+ ordering = ("manufacturer", "model")
1520
+ unique_together = [
1521
+ ("manufacturer", "model"),
1522
+ ]
1523
+
1524
+ def __str__(self):
1525
+ return self.model
1526
+
1527
+ def to_yaml(self):
1528
+ data = OrderedDict(
1529
+ (
1530
+ ("manufacturer", self.manufacturer.name),
1531
+ ("model", self.model),
1532
+ ("part_number", self.part_number),
1533
+ )
1534
+ )
1535
+
1536
+ # Component templates
1537
+ if self.console_port_templates.exists():
1538
+ data["console-ports"] = [
1539
+ {
1540
+ "name": c.name,
1541
+ "type": c.type,
1542
+ }
1543
+ for c in self.console_port_templates.all()
1544
+ ]
1545
+ if self.console_server_port_templates.exists():
1546
+ data["console-server-ports"] = [
1547
+ {
1548
+ "name": c.name,
1549
+ "type": c.type,
1550
+ }
1551
+ for c in self.console_server_port_templates.all()
1552
+ ]
1553
+ if self.power_port_templates.exists():
1554
+ data["power-ports"] = [
1555
+ {
1556
+ "name": c.name,
1557
+ "type": c.type,
1558
+ "maximum_draw": c.maximum_draw,
1559
+ "allocated_draw": c.allocated_draw,
1560
+ }
1561
+ for c in self.power_port_templates.all()
1562
+ ]
1563
+ if self.power_outlet_templates.exists():
1564
+ data["power-outlets"] = [
1565
+ {
1566
+ "name": c.name,
1567
+ "type": c.type,
1568
+ "power_port": c.power_port_template.name if c.power_port_template else None,
1569
+ "feed_leg": c.feed_leg,
1570
+ }
1571
+ for c in self.power_outlet_templates.all()
1572
+ ]
1573
+ if self.interface_templates.exists():
1574
+ data["interfaces"] = [
1575
+ {
1576
+ "name": c.name,
1577
+ "type": c.type,
1578
+ "mgmt_only": c.mgmt_only,
1579
+ }
1580
+ for c in self.interface_templates.all()
1581
+ ]
1582
+ if self.front_port_templates.exists():
1583
+ data["front-ports"] = [
1584
+ {
1585
+ "name": c.name,
1586
+ "type": c.type,
1587
+ "rear_port": c.rear_port_template.name,
1588
+ "rear_port_position": c.rear_port_position,
1589
+ }
1590
+ for c in self.front_port_templates.all()
1591
+ ]
1592
+ if self.rear_port_templates.exists():
1593
+ data["rear-ports"] = [
1594
+ {
1595
+ "name": c.name,
1596
+ "type": c.type,
1597
+ "positions": c.positions,
1598
+ }
1599
+ for c in self.rear_port_templates.all()
1600
+ ]
1601
+ if self.module_bay_templates.exists():
1602
+ data["module-bays"] = [
1603
+ {
1604
+ "name": c.name,
1605
+ "position": c.position,
1606
+ "label": c.label,
1607
+ "description": c.description,
1608
+ }
1609
+ for c in self.module_bay_templates.all()
1610
+ ]
1611
+
1612
+ return yaml.dump(dict(data), sort_keys=False, allow_unicode=True)
1613
+
1614
+ @property
1615
+ def display(self):
1616
+ return f"{self.manufacturer.name} {self.model}"
1617
+
1618
+
1619
+ @extras_features(
1620
+ "custom_links",
1621
+ "custom_validators",
1622
+ "export_templates",
1623
+ "graphql",
1624
+ "locations",
1625
+ "statuses",
1626
+ "webhooks",
1627
+ )
1628
+ class Module(PrimaryModel):
1629
+ """
1630
+ A Module represents a line card, supervisor, or other interchangeable hardware component within a ModuleBay.
1631
+ Each Module is assigned a ModuleType and Status, and optionally a Role and/or Tenant.
1632
+
1633
+ Each Module must be assigned to either a ModuleBay or a Location, but not both.
1634
+
1635
+ When a new Module is created, console, power and interface components are created along with it as dictated
1636
+ by the component templates assigned to its ModuleType. Components can also be added, modified, or deleted after
1637
+ the creation of a Module.
1638
+ """
1639
+
1640
+ module_type = models.ForeignKey(to="dcim.ModuleType", on_delete=models.PROTECT, related_name="modules")
1641
+ parent_module_bay = models.OneToOneField(
1642
+ to="dcim.ModuleBay",
1643
+ on_delete=models.CASCADE,
1644
+ related_name="installed_module",
1645
+ blank=True,
1646
+ null=True,
1647
+ )
1648
+ status = StatusField()
1649
+ role = RoleField(blank=True, null=True)
1650
+ tenant = models.ForeignKey(
1651
+ to="tenancy.Tenant",
1652
+ on_delete=models.PROTECT,
1653
+ related_name="modules",
1654
+ blank=True,
1655
+ null=True,
1656
+ )
1657
+ serial = models.CharField( # noqa: DJ001 # django-nullable-model-string-field -- intentional
1658
+ max_length=CHARFIELD_MAX_LENGTH,
1659
+ blank=True,
1660
+ null=True,
1661
+ verbose_name="Serial number",
1662
+ db_index=True,
1663
+ )
1664
+ asset_tag = models.CharField(
1665
+ max_length=CHARFIELD_MAX_LENGTH,
1666
+ blank=True,
1667
+ null=True,
1668
+ unique=True,
1669
+ verbose_name="Asset tag",
1670
+ help_text="A unique tag used to identify this module",
1671
+ )
1672
+ location = models.ForeignKey(
1673
+ to="dcim.Location",
1674
+ on_delete=models.PROTECT,
1675
+ related_name="modules",
1676
+ blank=True,
1677
+ null=True,
1678
+ )
1679
+ # TODO: add software support for Modules
1680
+
1681
+ clone_fields = [
1682
+ "module_type",
1683
+ "role",
1684
+ "tenant",
1685
+ "location",
1686
+ "status",
1687
+ ]
1688
+
1689
+ # The recursive nature of this model combined with the fact that it can be a child of a
1690
+ # device or location makes our natural key implementation unusable, so just use the pk
1691
+ natural_key_field_names = ["pk"]
1692
+
1693
+ class Meta:
1694
+ ordering = ("parent_module_bay", "location", "module_type", "asset_tag", "serial")
1695
+ constraints = [
1696
+ models.UniqueConstraint(
1697
+ fields=["module_type", "serial"],
1698
+ name="dcim_module_module_type_serial_unique",
1699
+ ),
1700
+ ]
1701
+
1702
+ def __str__(self):
1703
+ serial = f" (Serial: {self.serial})" if self.serial else ""
1704
+ asset_tag = f" (Asset Tag: {self.asset_tag})" if self.asset_tag else ""
1705
+ return str(self.module_type) + serial + asset_tag
1706
+
1707
+ @property
1708
+ def display(self):
1709
+ if self.location:
1710
+ return f"{self!s} at location {self.location}"
1711
+ elif self.parent_module_bay.parent_device is not None:
1712
+ return f"{self.module_type!s} installed in {self.parent_module_bay.parent_device.display}"
1713
+ else:
1714
+ return f"{self.module_type!s} installed in {self.parent_module_bay.parent_module.display}"
1715
+
1716
+ @property
1717
+ def device(self):
1718
+ """Walk up parent chain to find the Device that this Module is installed in, if one exists."""
1719
+ if self.parent_module_bay is None:
1720
+ return None
1721
+ return self.parent_module_bay.parent
1722
+
1723
+ def clean(self):
1724
+ super().clean()
1725
+
1726
+ # Validate that the Module is associated with a Location or a ModuleBay
1727
+ if self.parent_module_bay is None and self.location is None:
1728
+ raise ValidationError("One of location or parent_module_bay must be set")
1729
+
1730
+ # Validate location
1731
+ if self.location is not None:
1732
+ if self.parent_module_bay is not None:
1733
+ raise ValidationError("Only one of location or parent_module_bay must be set")
1734
+
1735
+ if ContentType.objects.get_for_model(self) not in self.location.location_type.content_types.all():
1736
+ raise ValidationError(
1737
+ {"location": f'Modules may not associate to locations of type "{self.location.location_type}".'}
1738
+ )
1739
+
1740
+ def save(self, *args, **kwargs):
1741
+ is_new = not self.present_in_database
1742
+
1743
+ if self.serial == "":
1744
+ self.serial = None
1745
+ if self.asset_tag == "":
1746
+ self.asset_tag = None
1747
+
1748
+ # Prevent creating a Module that is its own ancestor, creating an infinite loop
1749
+ parent_module = getattr(self.parent_module_bay, "parent_module", None)
1750
+ while parent_module is not None:
1751
+ if parent_module == self:
1752
+ raise ValidationError("Creating this instance would cause an infinite loop.")
1753
+ parent_module = getattr(parent_module.parent_module_bay, "parent_module", None)
1754
+
1755
+ # Keep track of whether the parent module bay has changed so we can update the component names
1756
+ parent_module_changed = (
1757
+ not is_new and not Module.objects.filter(pk=self.pk, parent_module_bay=self.parent_module_bay).exists()
1758
+ )
1759
+
1760
+ super().save(*args, **kwargs)
1761
+
1762
+ # If this is a new Module, instantiate all related components per the ModuleType definition
1763
+ if is_new:
1764
+ self.create_components()
1765
+
1766
+ # Render component names when this Module is first created or when the parent module bay has changed
1767
+ if is_new or parent_module_changed:
1768
+ self.render_component_names()
1769
+
1770
+ def create_components(self):
1771
+ """Create module components from the module type definition."""
1772
+ # The order of these is significant as
1773
+ # - PowerOutlet depends on PowerPort
1774
+ # - FrontPort depends on RearPort
1775
+ component_models = [
1776
+ (ConsolePort, self.module_type.console_port_templates.all()),
1777
+ (ConsoleServerPort, self.module_type.console_server_port_templates.all()),
1778
+ (PowerPort, self.module_type.power_port_templates.all()),
1779
+ (PowerOutlet, self.module_type.power_outlet_templates.all()),
1780
+ (Interface, self.module_type.interface_templates.all()),
1781
+ (RearPort, self.module_type.rear_port_templates.all()),
1782
+ (FrontPort, self.module_type.front_port_templates.all()),
1783
+ (ModuleBay, self.module_type.module_bay_templates.all()),
1784
+ ]
1785
+ instantiated_components = []
1786
+ for model, templates in component_models:
1787
+ model.objects.bulk_create([x.instantiate(device=None, module=self) for x in templates])
1788
+ return instantiated_components
1789
+
1790
+ def render_component_names(self):
1791
+ """
1792
+ Replace the {module}, {module.parent}, {module.parent.parent}, etc. template variables in descendant
1793
+ component names with the correct parent module bay positions.
1794
+ """
1795
+
1796
+ # disable sorting to improve performance, sorting isn't necessary here
1797
+ component_models = [
1798
+ self.console_ports.all().order_by(),
1799
+ self.console_server_ports.all().order_by(),
1800
+ self.power_ports.all().order_by(),
1801
+ self.power_outlets.all().order_by(),
1802
+ self.interfaces.all().order_by(),
1803
+ self.rear_ports.all().order_by(),
1804
+ self.front_ports.all().order_by(),
1805
+ ]
1806
+
1807
+ for component_qs in component_models:
1808
+ for component in component_qs.only("name", "module"):
1809
+ component.render_name_template(save=True)
1810
+
1811
+ for child in self.get_children():
1812
+ child.render_component_names()
1813
+
1814
+ def get_cables(self, pk_list=False):
1815
+ """
1816
+ Return a QuerySet or PK list matching all Cables connected to any component of this Module.
1817
+ """
1818
+ from .cables import Cable
1819
+
1820
+ cable_pks = []
1821
+ for component_model in [
1822
+ ConsolePort,
1823
+ ConsoleServerPort,
1824
+ PowerPort,
1825
+ PowerOutlet,
1826
+ Interface,
1827
+ FrontPort,
1828
+ RearPort,
1829
+ ]:
1830
+ cable_pks += component_model.objects.filter(module=self, cable__isnull=False).values_list(
1831
+ "cable", flat=True
1832
+ )
1833
+ if pk_list:
1834
+ return cable_pks
1835
+ return Cable.objects.filter(pk__in=cable_pks)
1836
+
1837
+ def get_children(self):
1838
+ """
1839
+ Return the set of child Modules installed in ModuleBays within this Module.
1840
+ """
1841
+ return Module.objects.filter(parent_module_bay__parent_module=self)
@@ -244,6 +244,44 @@ menu_items = (
244
244
  ),
245
245
  ),
246
246
  ),
247
+ NavMenuGroup(
248
+ name="Modules",
249
+ weight=250,
250
+ items=(
251
+ NavMenuItem(
252
+ link="dcim:module_list",
253
+ name="Modules",
254
+ weight=100,
255
+ permissions=[
256
+ "dcim.view_module",
257
+ ],
258
+ buttons=(
259
+ NavMenuAddButton(
260
+ link="dcim:module_add",
261
+ permissions=[
262
+ "dcim.add_module",
263
+ ],
264
+ ),
265
+ ),
266
+ ),
267
+ NavMenuItem(
268
+ link="dcim:moduletype_list",
269
+ name="Module Types",
270
+ weight=200,
271
+ permissions=[
272
+ "dcim.view_moduletype",
273
+ ],
274
+ buttons=(
275
+ NavMenuAddButton(
276
+ link="dcim:moduletype_add",
277
+ permissions=[
278
+ "dcim.add_moduletype",
279
+ ],
280
+ ),
281
+ ),
282
+ ),
283
+ ),
284
+ ),
247
285
  NavMenuGroup(
248
286
  name="Software",
249
287
  weight=300,
@@ -448,6 +486,15 @@ menu_items = (
448
486
  ],
449
487
  buttons=(),
450
488
  ),
489
+ NavMenuItem(
490
+ link="dcim:modulebay_list",
491
+ name="Module Bays",
492
+ weight=850,
493
+ permissions=[
494
+ "dcim.view_modulebay",
495
+ ],
496
+ buttons=(),
497
+ ),
451
498
  NavMenuItem(
452
499
  link="dcim:inventoryitem_list",
453
500
  name="Inventory Items",
nautobot/dcim/signals.py CHANGED
@@ -297,15 +297,15 @@ def prevent_adding_tagged_vlans_with_incorrect_mode_or_site(sender, instance, ac
297
297
  @receiver(post_save, sender=ControllerManagedDeviceGroup)
298
298
  def handle_controller_managed_device_group_controller_change(instance, raw=False, **_):
299
299
  """Update descendants when the top level `ControllerManagedDeviceGroup.controller` changes."""
300
- if instance.parent or instance._original_controller == instance.controller:
301
- return
302
-
303
300
  logger = logging.getLogger(__name__ + ".ControllerManagedDeviceGroup")
304
301
 
305
302
  if raw:
306
303
  logger.debug("Skipping controller update for imported controller device group %s", instance)
307
304
  return
308
305
 
306
+ if instance.parent or instance._original_controller == instance.controller:
307
+ return
308
+
309
309
  with transaction.atomic():
310
310
  for group in instance.descendants(include_self=False):
311
311
  group.controller = instance.controller