nautobot 2.2.9__py3-none-any.whl → 2.3.0__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 +113 -0
  26. nautobot/cloud/filters.py +187 -0
  27. nautobot/cloud/forms.py +339 -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 +246 -0
  32. nautobot/cloud/navigation.py +85 -0
  33. nautobot/cloud/tables.py +157 -0
  34. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
  35. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +122 -0
  36. nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
  37. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +111 -0
  38. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +69 -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 +125 -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 +95 -13
  57. nautobot/core/forms/fields.py +10 -4
  58. nautobot/core/forms/forms.py +11 -3
  59. nautobot/core/forms/widgets.py +18 -1
  60. nautobot/core/graphql/schema.py +26 -4
  61. nautobot/core/jobs/__init__.py +16 -2
  62. nautobot/core/jobs/cleanup.py +100 -0
  63. nautobot/core/jobs/groups.py +38 -0
  64. nautobot/core/management/commands/generate_test_data.py +116 -3
  65. nautobot/core/models/__init__.py +34 -9
  66. nautobot/core/models/generics.py +19 -3
  67. nautobot/core/models/name_color_content_types.py +7 -28
  68. nautobot/core/models/querysets.py +4 -3
  69. nautobot/core/models/tree_queries.py +1 -1
  70. nautobot/core/models/utils.py +21 -5
  71. nautobot/core/settings.py +2 -17
  72. nautobot/core/settings.yaml +34 -13
  73. nautobot/core/settings_funcs.py +103 -0
  74. nautobot/core/tables.py +130 -56
  75. nautobot/core/templates/admin/search_form.html +1 -1
  76. nautobot/core/templates/buttons/add.html +11 -3
  77. nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
  78. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
  79. nautobot/core/templates/buttons/export.html +101 -53
  80. nautobot/core/templates/buttons/job_import.html +11 -3
  81. nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
  82. nautobot/core/templates/generic/object_bulk_update.html +3 -1
  83. nautobot/core/templates/generic/object_changelog.html +0 -9
  84. nautobot/core/templates/generic/object_list.html +156 -17
  85. nautobot/core/templates/generic/object_retrieve.html +80 -16
  86. nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
  87. nautobot/core/templates/inc/javascript.html +2 -0
  88. nautobot/core/templates/inc/media.html +2 -2
  89. nautobot/core/templates/inc/nav_menu.html +1 -0
  90. nautobot/core/templates/inc/paginator.html +7 -7
  91. nautobot/core/templates/inc/search_panel.html +2 -2
  92. nautobot/core/templates/inc/table.html +2 -2
  93. nautobot/core/templates/nautobot_config.py.j2 +13 -8
  94. nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
  95. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
  96. nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
  97. nautobot/core/templates/utilities/theme_preview.html +25 -8
  98. nautobot/core/templates/utilities/worker_status.html +152 -0
  99. nautobot/core/templatetags/buttons.py +335 -38
  100. nautobot/core/templatetags/form_helpers.py +1 -1
  101. nautobot/core/templatetags/helpers.py +181 -11
  102. nautobot/core/testing/api.py +5 -4
  103. nautobot/core/testing/filters.py +63 -14
  104. nautobot/core/testing/mixins.py +46 -0
  105. nautobot/core/testing/models.py +22 -0
  106. nautobot/core/testing/schema.py +4 -8
  107. nautobot/core/testing/views.py +31 -14
  108. nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
  109. nautobot/core/tests/integration/test_swagger.py +1 -1
  110. nautobot/core/tests/nautobot_config.py +0 -1
  111. nautobot/core/tests/runner.py +2 -2
  112. nautobot/core/tests/test_api.py +1 -0
  113. nautobot/core/tests/test_authentication.py +7 -2
  114. nautobot/core/tests/test_filters.py +11 -9
  115. nautobot/core/tests/test_forms.py +9 -0
  116. nautobot/core/tests/test_graphql.py +27 -16
  117. nautobot/core/tests/test_jobs.py +122 -0
  118. nautobot/core/tests/test_tables.py +3 -1
  119. nautobot/core/tests/test_templatetags_helpers.py +12 -5
  120. nautobot/core/tests/test_utils.py +31 -20
  121. nautobot/core/tests/test_views.py +6 -6
  122. nautobot/core/urls.py +8 -3
  123. nautobot/core/utils/deprecation.py +29 -0
  124. nautobot/core/utils/filtering.py +12 -9
  125. nautobot/core/utils/lookup.py +37 -2
  126. nautobot/core/utils/requests.py +4 -1
  127. nautobot/core/views/__init__.py +137 -24
  128. nautobot/core/views/generic.py +119 -67
  129. nautobot/core/views/mixins.py +105 -36
  130. nautobot/core/views/paginator.py +9 -3
  131. nautobot/core/views/renderers.py +121 -56
  132. nautobot/core/views/utils.py +81 -1
  133. nautobot/dcim/__init__.py +0 -1
  134. nautobot/dcim/api/serializers.py +180 -44
  135. nautobot/dcim/api/urls.py +7 -3
  136. nautobot/dcim/api/views.py +53 -7
  137. nautobot/dcim/apps.py +3 -0
  138. nautobot/dcim/choices.py +25 -0
  139. nautobot/dcim/constants.py +7 -0
  140. nautobot/dcim/factory.py +252 -18
  141. nautobot/dcim/filters/__init__.py +373 -193
  142. nautobot/dcim/filters/mixins.py +274 -1
  143. nautobot/dcim/forms.py +834 -121
  144. nautobot/dcim/graphql/types.py +2 -2
  145. nautobot/dcim/homepage.py +1 -1
  146. nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
  147. nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
  148. nautobot/dcim/migrations/0061_module_models.py +862 -0
  149. nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
  150. nautobot/dcim/models/__init__.py +8 -0
  151. nautobot/dcim/models/cables.py +15 -0
  152. nautobot/dcim/models/device_component_templates.py +207 -53
  153. nautobot/dcim/models/device_components.py +275 -99
  154. nautobot/dcim/models/devices.py +468 -13
  155. nautobot/dcim/models/racks.py +0 -1
  156. nautobot/dcim/navigation.py +47 -0
  157. nautobot/dcim/signals.py +3 -3
  158. nautobot/dcim/tables/__init__.py +35 -23
  159. nautobot/dcim/tables/devices.py +229 -43
  160. nautobot/dcim/tables/devicetypes.py +65 -9
  161. nautobot/dcim/tables/racks.py +5 -1
  162. nautobot/dcim/tables/template_code.py +46 -26
  163. nautobot/dcim/templates/dcim/cable_connect.html +76 -3
  164. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
  165. nautobot/dcim/templates/dcim/device/base.html +14 -6
  166. nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
  167. nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
  168. nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
  169. nautobot/dcim/templates/dcim/device/frontports.html +2 -3
  170. nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
  171. nautobot/dcim/templates/dcim/device/inventory.html +2 -3
  172. nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
  173. nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
  174. nautobot/dcim/templates/dcim/device/powerports.html +2 -3
  175. nautobot/dcim/templates/dcim/device/rearports.html +2 -3
  176. nautobot/dcim/templates/dcim/device.html +45 -1
  177. nautobot/dcim/templates/dcim/device_component.html +13 -5
  178. nautobot/dcim/templates/dcim/device_list.html +2 -1
  179. nautobot/dcim/templates/dcim/devicetype.html +99 -98
  180. nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
  181. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  182. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
  183. nautobot/dcim/templates/dcim/interface.html +17 -2
  184. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
  185. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  186. nautobot/dcim/templates/dcim/manufacturer.html +24 -0
  187. nautobot/dcim/templates/dcim/module/base.html +97 -0
  188. nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
  189. nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
  190. nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
  191. nautobot/dcim/templates/dcim/module_destroy.html +5 -0
  192. nautobot/dcim/templates/dcim/module_frontports.html +53 -0
  193. nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
  194. nautobot/dcim/templates/dcim/module_list.html +20 -0
  195. nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
  196. nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
  197. nautobot/dcim/templates/dcim/module_powerports.html +53 -0
  198. nautobot/dcim/templates/dcim/module_rearports.html +53 -0
  199. nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
  200. nautobot/dcim/templates/dcim/module_update.html +71 -0
  201. nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
  202. nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
  203. nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
  204. nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
  205. nautobot/dcim/templates/dcim/moduletype_retrieve.html +159 -0
  206. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
  207. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
  208. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  209. nautobot/dcim/tests/test_api.py +693 -208
  210. nautobot/dcim/tests/test_filters.py +843 -217
  211. nautobot/dcim/tests/test_models.py +1072 -8
  212. nautobot/dcim/tests/test_views.py +1510 -341
  213. nautobot/dcim/urls.py +17 -2
  214. nautobot/dcim/utils.py +2 -3
  215. nautobot/dcim/views.py +1106 -116
  216. nautobot/extras/__init__.py +0 -1
  217. nautobot/extras/api/serializers.py +115 -3
  218. nautobot/extras/api/urls.py +12 -0
  219. nautobot/extras/api/views.py +66 -0
  220. nautobot/extras/apps.py +2 -2
  221. nautobot/extras/choices.py +43 -0
  222. nautobot/extras/context_managers.py +13 -8
  223. nautobot/extras/datasources/git.py +2 -0
  224. nautobot/extras/factory.py +460 -9
  225. nautobot/extras/filters/__init__.py +174 -3
  226. nautobot/extras/filters/mixins.py +46 -43
  227. nautobot/extras/forms/base.py +24 -5
  228. nautobot/extras/forms/forms.py +227 -8
  229. nautobot/extras/forms/mixins.py +93 -0
  230. nautobot/extras/graphql/types.py +23 -10
  231. nautobot/extras/homepage.py +14 -1
  232. nautobot/extras/management/__init__.py +1 -0
  233. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
  234. nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
  235. nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
  236. nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
  237. nautobot/extras/migrations/0111_metadata.py +162 -0
  238. nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
  239. nautobot/extras/migrations/0113_saved_views.py +77 -0
  240. nautobot/extras/models/__init__.py +15 -1
  241. nautobot/extras/models/change_logging.py +3 -3
  242. nautobot/extras/models/contacts.py +4 -0
  243. nautobot/extras/models/customfields.py +18 -3
  244. nautobot/extras/models/groups.py +389 -225
  245. nautobot/extras/models/jobs.py +6 -3
  246. nautobot/extras/models/metadata.py +441 -0
  247. nautobot/extras/models/mixins.py +72 -62
  248. nautobot/extras/models/models.py +118 -9
  249. nautobot/extras/models/relationships.py +9 -2
  250. nautobot/extras/models/tags.py +13 -2
  251. nautobot/extras/navigation.py +57 -0
  252. nautobot/extras/plugins/__init__.py +3 -1
  253. nautobot/extras/querysets.py +30 -66
  254. nautobot/extras/signals.py +95 -100
  255. nautobot/extras/tables.py +165 -12
  256. nautobot/extras/templates/extras/dynamicgroup.html +44 -15
  257. nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
  258. nautobot/extras/templates/extras/job.html +1 -1
  259. nautobot/extras/templates/extras/jobresult.html +61 -74
  260. nautobot/extras/templates/extras/metadatatype_create.html +89 -0
  261. nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
  262. nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
  263. nautobot/extras/templates/extras/objectchange_list.html +0 -12
  264. nautobot/extras/templates/extras/plugins_list.html +1 -3
  265. nautobot/extras/templates/extras/role_retrieve.html +48 -0
  266. nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
  267. nautobot/extras/tests/integration/test_customfields.py +1 -0
  268. nautobot/extras/tests/test_api.py +509 -23
  269. nautobot/extras/tests/test_changelog.py +20 -9
  270. nautobot/extras/tests/test_context_managers.py +22 -15
  271. nautobot/extras/tests/test_datasources.py +13 -1
  272. nautobot/extras/tests/test_dynamicgroups.py +201 -171
  273. nautobot/extras/tests/test_filters.py +211 -12
  274. nautobot/extras/tests/test_jobs.py +6 -6
  275. nautobot/extras/tests/test_models.py +501 -4
  276. nautobot/extras/tests/test_relationships.py +1 -0
  277. nautobot/extras/tests/test_views.py +565 -8
  278. nautobot/extras/tests/test_webhooks.py +1 -1
  279. nautobot/extras/urls.py +5 -0
  280. nautobot/extras/utils.py +51 -11
  281. nautobot/extras/views.py +542 -76
  282. nautobot/ipam/__init__.py +0 -1
  283. nautobot/ipam/apps.py +1 -0
  284. nautobot/ipam/factory.py +17 -19
  285. nautobot/ipam/filters.py +13 -0
  286. nautobot/ipam/forms.py +8 -4
  287. nautobot/ipam/graphql/types.py +2 -2
  288. nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
  289. nautobot/ipam/models.py +11 -8
  290. nautobot/ipam/querysets.py +1 -1
  291. nautobot/ipam/signals.py +4 -2
  292. nautobot/ipam/tables.py +5 -0
  293. nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
  294. nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
  295. nautobot/ipam/templates/ipam/prefix.html +1 -0
  296. nautobot/ipam/tests/test_api.py +37 -18
  297. nautobot/ipam/tests/test_filters.py +26 -2
  298. nautobot/ipam/tests/test_models.py +6 -0
  299. nautobot/ipam/tests/test_querysets.py +1 -1
  300. nautobot/ipam/tests/test_views.py +3 -2
  301. nautobot/ipam/urls.py +2 -2
  302. nautobot/ipam/views.py +18 -26
  303. nautobot/project-static/css/base.css +20 -0
  304. nautobot/project-static/css/dark.css +11 -0
  305. nautobot/project-static/docs/404.html +892 -88
  306. nautobot/project-static/docs/apps/index.html +892 -88
  307. nautobot/project-static/docs/apps/nautobot-apps.html +892 -88
  308. nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
  309. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +1 -0
  310. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
  311. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +919 -120
  312. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +904 -101
  313. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1618 -903
  314. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +935 -144
  315. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +977 -188
  316. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +901 -99
  317. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +897 -93
  318. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +991 -193
  319. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +974 -131
  320. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1078 -272
  321. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1242 -334
  322. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1727 -875
  323. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1164 -381
  324. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2088 -1374
  325. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2246 -1422
  326. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +912 -111
  327. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +963 -163
  328. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1010 -223
  329. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1913 -1277
  330. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1846 -1102
  331. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +904 -101
  332. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2331 -1699
  333. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1802 -1024
  334. nautobot/project-static/docs/development/apps/api/configuration-view.html +892 -88
  335. nautobot/project-static/docs/development/apps/api/database-backend-config.html +892 -88
  336. nautobot/project-static/docs/development/apps/api/models/django-admin.html +892 -88
  337. nautobot/project-static/docs/development/apps/api/models/global-search.html +892 -88
  338. nautobot/project-static/docs/development/apps/api/models/graphql.html +892 -88
  339. nautobot/project-static/docs/development/apps/api/models/index.html +942 -90
  340. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +892 -88
  341. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +892 -88
  342. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +892 -88
  343. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +892 -88
  344. nautobot/project-static/docs/development/apps/api/platform-features/index.html +892 -88
  345. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +892 -88
  346. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +892 -88
  347. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +892 -88
  348. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +892 -88
  349. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +892 -88
  350. nautobot/project-static/docs/development/apps/api/prometheus.html +892 -88
  351. nautobot/project-static/docs/development/apps/api/setup.html +892 -88
  352. nautobot/project-static/docs/development/apps/api/testing.html +892 -88
  353. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +892 -88
  354. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +892 -88
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +892 -88
  356. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +892 -88
  357. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +892 -88
  358. nautobot/project-static/docs/development/apps/api/views/base-template.html +892 -88
  359. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +892 -88
  360. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +892 -88
  361. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +892 -88
  362. nautobot/project-static/docs/development/apps/api/views/index.html +892 -88
  363. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +892 -88
  364. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +892 -88
  365. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +892 -88
  366. nautobot/project-static/docs/development/apps/api/views/notes.html +892 -88
  367. nautobot/project-static/docs/development/apps/api/views/rest-api.html +892 -88
  368. nautobot/project-static/docs/development/apps/api/views/urls.html +892 -88
  369. nautobot/project-static/docs/development/apps/index.html +892 -88
  370. nautobot/project-static/docs/development/apps/migration/code-updates.html +892 -88
  371. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +892 -88
  372. nautobot/project-static/docs/development/apps/migration/from-v1.html +892 -88
  373. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +892 -88
  374. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +892 -88
  375. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +892 -88
  376. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +892 -88
  377. nautobot/project-static/docs/development/apps/porting-from-netbox.html +892 -88
  378. nautobot/project-static/docs/development/core/application-registry.html +892 -88
  379. nautobot/project-static/docs/development/core/best-practices.html +893 -88
  380. nautobot/project-static/docs/development/core/bootstrap-ui.html +892 -88
  381. nautobot/project-static/docs/development/core/caching.html +892 -88
  382. nautobot/project-static/docs/development/core/controllers.html +892 -88
  383. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +892 -88
  384. nautobot/project-static/docs/development/core/generic-views.html +892 -88
  385. nautobot/project-static/docs/development/core/getting-started.html +892 -88
  386. nautobot/project-static/docs/development/core/homepage.html +892 -88
  387. nautobot/project-static/docs/development/core/index.html +892 -88
  388. nautobot/project-static/docs/development/core/model-checklist.html +901 -89
  389. nautobot/project-static/docs/development/core/model-features.html +892 -88
  390. nautobot/project-static/docs/development/core/natural-keys.html +892 -88
  391. nautobot/project-static/docs/development/core/navigation-menu.html +892 -88
  392. nautobot/project-static/docs/development/core/release-checklist.html +895 -91
  393. nautobot/project-static/docs/development/core/role-internals.html +892 -88
  394. nautobot/project-static/docs/development/core/settings.html +892 -88
  395. nautobot/project-static/docs/development/core/style-guide.html +893 -89
  396. nautobot/project-static/docs/development/core/templates.html +904 -89
  397. nautobot/project-static/docs/development/core/testing.html +892 -88
  398. nautobot/project-static/docs/development/core/user-preferences.html +892 -88
  399. nautobot/project-static/docs/development/index.html +892 -88
  400. nautobot/project-static/docs/development/jobs/index.html +893 -89
  401. nautobot/project-static/docs/development/jobs/migration/from-v1.html +892 -88
  402. nautobot/project-static/docs/index.html +892 -88
  403. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
  404. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
  405. nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
  406. nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
  407. nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
  408. nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
  409. nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
  410. nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
  411. nautobot/project-static/docs/models/dcim/module.html +15 -0
  412. nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
  413. nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
  414. nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
  415. nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
  416. nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
  417. nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
  418. nautobot/project-static/docs/models/extras/role.html +15 -0
  419. nautobot/project-static/docs/models/extras/savedview.html +15 -0
  420. nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
  421. nautobot/project-static/docs/models/extras/status.html +15 -0
  422. nautobot/project-static/docs/objects.inv +0 -0
  423. nautobot/project-static/docs/overview/application_stack.html +900 -89
  424. nautobot/project-static/docs/overview/design_philosophy.html +892 -88
  425. nautobot/project-static/docs/release-notes/index.html +1129 -92
  426. nautobot/project-static/docs/release-notes/version-1.0.html +892 -88
  427. nautobot/project-static/docs/release-notes/version-1.1.html +892 -88
  428. nautobot/project-static/docs/release-notes/version-1.2.html +892 -88
  429. nautobot/project-static/docs/release-notes/version-1.3.html +892 -88
  430. nautobot/project-static/docs/release-notes/version-1.4.html +892 -88
  431. nautobot/project-static/docs/release-notes/version-1.5.html +893 -89
  432. nautobot/project-static/docs/release-notes/version-1.6.html +893 -89
  433. nautobot/project-static/docs/release-notes/version-2.0.html +892 -88
  434. nautobot/project-static/docs/release-notes/version-2.1.html +892 -88
  435. nautobot/project-static/docs/release-notes/version-2.2.html +895 -91
  436. nautobot/project-static/docs/release-notes/version-2.3.html +9954 -0
  437. nautobot/project-static/docs/requirements.txt +5 -5
  438. nautobot/project-static/docs/search/search_index.json +1 -1
  439. nautobot/project-static/docs/sitemap.xml +331 -256
  440. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  441. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +892 -88
  442. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +892 -88
  443. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +892 -88
  444. nautobot/project-static/docs/user-guide/administration/configuration/index.html +892 -88
  445. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +992 -174
  446. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +892 -88
  447. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +892 -88
  448. nautobot/project-static/docs/user-guide/administration/guides/caching.html +892 -88
  449. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +896 -88
  450. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +892 -88
  451. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +892 -88
  452. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +892 -88
  453. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +892 -88
  454. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +892 -88
  455. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +892 -88
  456. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +892 -88
  457. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +892 -88
  458. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +944 -153
  459. nautobot/project-static/docs/user-guide/administration/installation/index.html +901 -93
  460. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +934 -122
  461. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +954 -157
  462. nautobot/project-static/docs/user-guide/administration/installation/services.html +913 -112
  463. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +908 -99
  464. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +892 -88
  465. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +892 -88
  466. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +892 -88
  467. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +892 -88
  468. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +893 -89
  469. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +892 -88
  470. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +892 -88
  471. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +892 -88
  472. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +892 -88
  473. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +892 -88
  474. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +892 -88
  475. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +892 -88
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +892 -88
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +892 -88
  478. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +892 -88
  479. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +892 -88
  480. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +893 -89
  481. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +892 -88
  482. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +896 -88
  483. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +895 -91
  484. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
  485. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
  486. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
  487. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
  488. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
  489. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
  491. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +906 -102
  492. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +923 -105
  493. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +923 -105
  494. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +918 -100
  495. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +923 -105
  496. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +906 -102
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +906 -102
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +913 -105
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +920 -116
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +921 -117
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +918 -114
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +906 -102
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +914 -105
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +926 -108
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +936 -118
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +928 -106
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +906 -102
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +937 -119
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +928 -110
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +918 -114
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +921 -117
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +923 -115
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +916 -112
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +906 -102
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +940 -83
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +924 -106
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +906 -102
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +943 -86
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +921 -103
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +929 -125
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +918 -114
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +906 -102
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +922 -104
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +924 -106
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +906 -102
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +906 -102
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +906 -102
  532. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +936 -88
  533. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +892 -88
  534. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +897 -89
  535. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +897 -89
  536. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +892 -88
  537. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +892 -88
  538. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +892 -88
  539. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +892 -88
  540. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +892 -88
  541. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +892 -88
  542. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +892 -88
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +892 -88
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +892 -88
  545. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +892 -88
  546. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +901 -96
  547. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +892 -88
  548. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +892 -88
  549. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +892 -88
  550. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +892 -88
  551. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +892 -88
  552. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +897 -89
  553. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +892 -88
  554. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +892 -88
  555. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +892 -88
  556. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +892 -88
  557. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +892 -88
  558. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +892 -88
  559. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +892 -88
  560. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +892 -88
  561. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +892 -88
  562. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +892 -88
  563. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +892 -88
  564. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +892 -88
  565. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +892 -88
  566. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
  567. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
  568. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
  569. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
  570. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
  571. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
  572. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
  573. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
  574. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
  575. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
  576. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
  587. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
  589. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
  590. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
  591. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +892 -88
  592. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +892 -88
  593. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +892 -88
  594. nautobot/project-static/docs/user-guide/index.html +892 -88
  595. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +892 -88
  596. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +892 -88
  597. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +892 -88
  598. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +892 -88
  599. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1258 -785
  600. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +895 -91
  601. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +892 -88
  602. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +892 -88
  603. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +892 -88
  604. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +892 -88
  605. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +892 -88
  606. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +892 -88
  607. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +892 -88
  608. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +892 -88
  609. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +892 -88
  610. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +896 -88
  611. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +892 -88
  612. nautobot/project-static/docs/user-guide/platform-functionality/note.html +895 -91
  613. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +9061 -0
  614. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +895 -91
  615. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +892 -88
  616. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +892 -88
  617. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +892 -88
  618. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +892 -88
  619. nautobot/project-static/docs/user-guide/platform-functionality/role.html +895 -91
  620. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
  621. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +895 -91
  622. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
  623. nautobot/project-static/docs/user-guide/platform-functionality/status.html +892 -88
  624. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +892 -88
  625. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +950 -121
  626. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +892 -88
  627. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +892 -88
  628. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +892 -88
  629. nautobot/project-static/js/forms.js +71 -0
  630. nautobot/project-static/js/table_sorting_indicator.js +46 -0
  631. nautobot/project-static/js/tableconfig.js +6 -1
  632. nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
  633. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
  634. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
  635. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
  636. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
  637. nautobot/tenancy/__init__.py +0 -1
  638. nautobot/tenancy/apps.py +1 -0
  639. nautobot/tenancy/factory.py +3 -2
  640. nautobot/tenancy/filters/__init__.py +1 -0
  641. nautobot/tenancy/forms.py +1 -1
  642. nautobot/tenancy/templates/tenancy/tenant.html +24 -20
  643. nautobot/tenancy/views.py +11 -10
  644. nautobot/users/__init__.py +0 -1
  645. nautobot/users/api/serializers.py +1 -1
  646. nautobot/users/api/views.py +4 -2
  647. nautobot/users/apps.py +3 -2
  648. nautobot/users/factory.py +3 -3
  649. nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
  650. nautobot/users/models.py +12 -0
  651. nautobot/users/tests/test_filters.py +6 -3
  652. nautobot/users/urls.py +8 -0
  653. nautobot/virtualization/__init__.py +0 -1
  654. nautobot/virtualization/apps.py +1 -0
  655. nautobot/virtualization/filters.py +6 -1
  656. nautobot/virtualization/forms.py +11 -3
  657. nautobot/virtualization/graphql/types.py +2 -2
  658. nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
  659. nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
  660. nautobot/virtualization/models.py +0 -2
  661. nautobot/virtualization/tables.py +10 -3
  662. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  663. nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
  664. nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
  665. nautobot/virtualization/tests/test_api.py +9 -4
  666. nautobot/virtualization/tests/test_filters.py +22 -0
  667. nautobot/virtualization/tests/test_models.py +7 -3
  668. nautobot/virtualization/tests/test_views.py +19 -3
  669. nautobot/virtualization/urls.py +2 -2
  670. nautobot/virtualization/views.py +10 -32
  671. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/METADATA +20 -18
  672. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/RECORD +677 -557
  673. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +0 -1
  674. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +0 -1
  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.0.dist-info}/LICENSE.txt +0 -0
  695. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/NOTICE +0 -0
  696. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/WHEEL +0 -0
  697. {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,5 @@
1
+ import re
2
+
1
3
  from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
2
4
  from django.contrib.contenttypes.models import ContentType
3
5
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
@@ -36,6 +38,7 @@ from nautobot.dcim.constants import (
36
38
  from nautobot.extras.models import (
37
39
  ChangeLoggedModel,
38
40
  RelationshipModel,
41
+ RoleField,
39
42
  Status,
40
43
  StatusField,
41
44
  )
@@ -52,6 +55,7 @@ __all__ = (
52
55
  "InterfaceRedundancyGroup",
53
56
  "InterfaceRedundancyGroupAssociation",
54
57
  "InventoryItem",
58
+ "ModuleBay",
55
59
  "PathEndpoint",
56
60
  "PowerOutlet",
57
61
  "PowerPort",
@@ -70,7 +74,7 @@ class ComponentModel(PrimaryModel):
70
74
  label = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True, help_text="Physical label")
71
75
  description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
72
76
 
73
- natural_key_field_names = ["name", "device"]
77
+ natural_key_field_names = ["device", "name"]
74
78
 
75
79
  class Meta:
76
80
  abstract = True
@@ -84,6 +88,9 @@ class ComponentModel(PrimaryModel):
84
88
  """
85
89
  Return a new ObjectChange with the `related_object` pinned to the `device` by default.
86
90
  """
91
+ if "related_object" in kwargs:
92
+ return super().to_objectchange(action, **kwargs)
93
+
87
94
  # Annotate the parent Device
88
95
  try:
89
96
  device = self.device
@@ -93,6 +100,114 @@ class ComponentModel(PrimaryModel):
93
100
 
94
101
  return super().to_objectchange(action, related_object=device, **kwargs)
95
102
 
103
+ @property
104
+ def parent(self):
105
+ return self.device
106
+
107
+
108
+ class ModularComponentModel(ComponentModel):
109
+ device = ForeignKeyWithAutoRelatedName(
110
+ to="dcim.Device",
111
+ on_delete=models.CASCADE,
112
+ blank=True,
113
+ null=True,
114
+ )
115
+ module = ForeignKeyWithAutoRelatedName(
116
+ to="dcim.Module",
117
+ on_delete=models.CASCADE,
118
+ blank=True,
119
+ null=True,
120
+ )
121
+
122
+ natural_key_field_names = ["device", "module", "name"]
123
+
124
+ class Meta:
125
+ abstract = True
126
+ ordering = ("device", "module", "_name")
127
+ constraints = [
128
+ models.UniqueConstraint(
129
+ fields=("device", "name"),
130
+ name="%(app_label)s_%(class)s_device_name_unique",
131
+ ),
132
+ models.UniqueConstraint(
133
+ fields=("module", "name"),
134
+ name="%(app_label)s_%(class)s_module_name_unique",
135
+ ),
136
+ ]
137
+
138
+ @property
139
+ def parent(self):
140
+ """Device that this component belongs to, walking up module inheritance if necessary."""
141
+ return self.module.device if self.module else self.device
142
+
143
+ def render_name_template(self, save=False):
144
+ """
145
+ Replace the {module}, {module.parent}, {module.parent.parent}, etc. variables in the name
146
+ field with the actual module bay positions.
147
+
148
+ Args:
149
+ save (bool, optional): If True, save the object after updating the name field. Defaults to False.
150
+
151
+ If a module bay position is blank, it will be skipped and the parents will be checked until a non-blank
152
+ position is found. If all parent module bays are exhausted, the variable is left as-is.
153
+
154
+ Example:
155
+ - Device (name="Device 1")
156
+ - ModuleBay (position="A")
157
+ - Module
158
+ - ModuleBay (position="")
159
+ - Module
160
+ - Interface (name="{module}{module.parent}")
161
+
162
+ The deeply nested interface would be named "A{module.parent}" after calling this method.
163
+ """
164
+ if self.module and self.module.parent_module_bay and "{module" in self.name:
165
+ name = ""
166
+ module_bay = self.module.parent_module_bay
167
+ positions = []
168
+ while module_bay is not None:
169
+ position = getattr(module_bay, "position", None)
170
+ if position:
171
+ positions.append(position)
172
+ module_bay = getattr(getattr(module_bay, "parent_module", None), "parent_module_bay", None)
173
+ for part in re.split(r"({module(?:\.parent)*})", self.name):
174
+ if re.fullmatch(r"{module(\.parent)*}", part):
175
+ depth = part.count(".parent")
176
+ if depth < len(positions):
177
+ name += positions[depth]
178
+ continue
179
+ name += part
180
+ if self.name != name:
181
+ self.name = name
182
+ if save:
183
+ self.save(update_fields=["_name", "name"])
184
+
185
+ def to_objectchange(self, action, **kwargs):
186
+ """
187
+ Return a new ObjectChange with the `related_object` pinned to the parent `device` or `module`.
188
+ """
189
+ if "related_object" in kwargs:
190
+ return super().to_objectchange(action, **kwargs)
191
+
192
+ # Annotate the parent
193
+ try:
194
+ parent = self.device if self.device else self.module
195
+ except ObjectDoesNotExist:
196
+ # The parent may have already been deleted
197
+ parent = None
198
+
199
+ return super().to_objectchange(action, related_object=parent, **kwargs)
200
+
201
+ def clean(self):
202
+ super().clean()
203
+
204
+ # Validate that a Device or Module is set, but not both
205
+ if self.device and self.module:
206
+ raise ValidationError("Only one of device or module must be set")
207
+
208
+ if not (self.device or self.module):
209
+ raise ValidationError("Either device or module must be set")
210
+
96
211
 
97
212
  class CableTermination(models.Model):
98
213
  """
@@ -212,9 +327,9 @@ class PathEndpoint(models.Model):
212
327
  "graphql",
213
328
  "webhooks",
214
329
  )
215
- class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
330
+ class ConsolePort(ModularComponentModel, CableTermination, PathEndpoint):
216
331
  """
217
- A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
332
+ A physical console port within a Device or Module. ConsolePorts connect to ConsoleServerPorts.
218
333
  """
219
334
 
220
335
  type = models.CharField(
@@ -224,14 +339,6 @@ class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
224
339
  help_text="Physical port type",
225
340
  )
226
341
 
227
- class Meta:
228
- ordering = ("device", "_name")
229
- unique_together = ("device", "name")
230
-
231
- @property
232
- def parent(self):
233
- return self.device
234
-
235
342
 
236
343
  #
237
344
  # Console server ports
@@ -239,9 +346,9 @@ class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
239
346
 
240
347
 
241
348
  @extras_features("custom_links", "cable_terminations", "custom_validators", "graphql", "webhooks")
242
- class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
349
+ class ConsoleServerPort(ModularComponentModel, CableTermination, PathEndpoint):
243
350
  """
244
- A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
351
+ A physical port within a Device or Module (typically a designated console server) which provides access to ConsolePorts.
245
352
  """
246
353
 
247
354
  type = models.CharField(
@@ -251,14 +358,6 @@ class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
251
358
  help_text="Physical port type",
252
359
  )
253
360
 
254
- class Meta:
255
- ordering = ("device", "_name")
256
- unique_together = ("device", "name")
257
-
258
- @property
259
- def parent(self):
260
- return self.device
261
-
262
361
 
263
362
  #
264
363
  # Power ports
@@ -273,9 +372,9 @@ class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
273
372
  "graphql",
274
373
  "webhooks",
275
374
  )
276
- class PowerPort(CableTermination, PathEndpoint, ComponentModel):
375
+ class PowerPort(ModularComponentModel, CableTermination, PathEndpoint):
277
376
  """
278
- A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
377
+ A physical power supply (intake) port within a Device or Module. PowerPorts connect to PowerOutlets.
279
378
  """
280
379
 
281
380
  type = models.CharField(
@@ -297,14 +396,6 @@ class PowerPort(CableTermination, PathEndpoint, ComponentModel):
297
396
  help_text="Allocated power draw (watts)",
298
397
  )
299
398
 
300
- class Meta:
301
- ordering = ("device", "_name")
302
- unique_together = ("device", "name")
303
-
304
- @property
305
- def parent(self):
306
- return self.device
307
-
308
399
  def clean(self):
309
400
  super().clean()
310
401
 
@@ -384,9 +475,9 @@ class PowerPort(CableTermination, PathEndpoint, ComponentModel):
384
475
 
385
476
 
386
477
  @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
387
- class PowerOutlet(CableTermination, PathEndpoint, ComponentModel):
478
+ class PowerOutlet(ModularComponentModel, CableTermination, PathEndpoint):
388
479
  """
389
- A physical power outlet (output) within a Device which provides power to a PowerPort.
480
+ A physical power outlet (output) within a Device or Module which provides power to a PowerPort.
390
481
  """
391
482
 
392
483
  type = models.CharField(
@@ -410,19 +501,11 @@ class PowerOutlet(CableTermination, PathEndpoint, ComponentModel):
410
501
  help_text="Phase (for three-phase feeds)",
411
502
  )
412
503
 
413
- class Meta:
414
- ordering = ("device", "_name")
415
- unique_together = ("device", "name")
416
-
417
- @property
418
- def parent(self):
419
- return self.device
420
-
421
504
  def clean(self):
422
505
  super().clean()
423
506
 
424
507
  # Validate power port assignment
425
- if self.power_port and self.power_port.device != self.device:
508
+ if self.power_port and self.parent and self.power_port.parent != self.parent:
426
509
  raise ValidationError(f"Parent power port ({self.power_port}) must belong to the same device")
427
510
 
428
511
 
@@ -446,6 +529,7 @@ class BaseInterface(RelationshipModel):
446
529
  validators=[MinValueValidator(1), MaxValueValidator(65536)],
447
530
  verbose_name="MTU",
448
531
  )
532
+ role = RoleField(blank=True, null=True)
449
533
  mode = models.CharField(max_length=50, choices=InterfaceModeChoices, blank=True)
450
534
  parent_interface = models.ForeignKey(
451
535
  to="self",
@@ -500,9 +584,9 @@ class BaseInterface(RelationshipModel):
500
584
  "statuses",
501
585
  "webhooks",
502
586
  )
503
- class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
587
+ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInterface):
504
588
  """
505
- A network interface within a Device. A physical Interface can connect to exactly one other Interface.
589
+ A network interface within a Device or Module. A physical Interface can connect to exactly one other Interface.
506
590
  """
507
591
 
508
592
  # Override ComponentModel._name to specify naturalize_interface function
@@ -559,15 +643,14 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
559
643
  verbose_name="IP Addresses",
560
644
  )
561
645
 
562
- class Meta:
563
- ordering = ("device", CollateAsChar("_name"))
564
- unique_together = ("device", "name")
646
+ class Meta(ModularComponentModel.Meta):
647
+ ordering = ("device", "module", CollateAsChar("_name"))
565
648
 
566
649
  def clean(self):
567
650
  super().clean()
568
651
 
569
652
  # VRF validation
570
- if self.vrf and self.vrf not in self.device.vrfs.all():
653
+ if self.vrf and self.parent and self.vrf not in self.parent.vrfs.all():
571
654
  # TODO(jathan): Or maybe we automatically add the VRF to the device?
572
655
  raise ValidationError({"vrf": "VRF must be assigned to same Device."})
573
656
 
@@ -578,19 +661,23 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
578
661
  raise ValidationError({"lag": "A LAG interface cannot be its own parent."})
579
662
 
580
663
  # An interface's LAG must belong to the same device or virtual chassis
581
- if self.lag.device_id != self.device_id:
582
- if self.device.virtual_chassis is None:
664
+ if self.parent and self.lag.parent != self.parent:
665
+ if self.lag.parent is None:
666
+ raise ValidationError(
667
+ {"lag": f"The selected LAG interface ({self.lag}) does not belong to a device."}
668
+ )
669
+ elif self.parent.virtual_chassis is None:
583
670
  raise ValidationError(
584
671
  {
585
- "lag": f"The selected LAG interface ({self.lag}) belongs to a different device ({self.lag.device})."
672
+ "lag": f"The selected LAG interface ({self.lag}) belongs to a different device ({self.lag.parent})."
586
673
  }
587
674
  )
588
- elif self.lag.device.virtual_chassis_id != self.device.virtual_chassis_id:
675
+ elif self.lag.parent.virtual_chassis_id != self.parent.virtual_chassis_id:
589
676
  raise ValidationError(
590
677
  {
591
678
  "lag": (
592
- f"The selected LAG interface ({self.lag}) belongs to {self.lag.device}, which is not part "
593
- f"of virtual chassis {self.device.virtual_chassis}."
679
+ f"The selected LAG interface ({self.lag}) belongs to {self.lag.parent}, which is not part "
680
+ f"of virtual chassis {self.parent.virtual_chassis}."
594
681
  )
595
682
  }
596
683
  )
@@ -623,19 +710,19 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
623
710
  )
624
711
 
625
712
  # An interface's parent must belong to the same device or virtual chassis
626
- if self.parent_interface.device != self.device:
627
- if getattr(self.device, "virtual_chassis", None) is None:
713
+ if self.parent and self.parent_interface.parent != self.parent:
714
+ if getattr(self.parent, "virtual_chassis", None) is None:
628
715
  raise ValidationError(
629
716
  {
630
717
  "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs to a different device "
631
- f"({self.parent_interface.device})."
718
+ f"({self.parent_interface.parent})."
632
719
  }
633
720
  )
634
- elif self.parent_interface.device.virtual_chassis != self.device.virtual_chassis:
721
+ elif self.parent_interface.parent.virtual_chassis != self.parent.virtual_chassis:
635
722
  raise ValidationError(
636
723
  {
637
- "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs to {self.parent_interface.device}, which "
638
- f"is not part of virtual chassis {self.device.virtual_chassis}."
724
+ "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs to {self.parent_interface.parent}, which "
725
+ f"is not part of virtual chassis {self.parent.virtual_chassis}."
639
726
  }
640
727
  )
641
728
 
@@ -645,7 +732,8 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
645
732
  if (
646
733
  self.untagged_vlan
647
734
  and self.untagged_vlan.locations.exists()
648
- and not self.untagged_vlan.locations.filter(id=self.device.location_id).exists()
735
+ and self.parent
736
+ and not self.untagged_vlan.locations.filter(id=self.parent.location_id).exists()
649
737
  ):
650
738
  raise ValidationError(
651
739
  {
@@ -663,22 +751,22 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
663
751
  raise ValidationError({"bridge": "An interface cannot be bridged to itself."})
664
752
 
665
753
  # A bridged interface belong to the same device or virtual chassis
666
- if self.bridge.device.id != self.device.id:
667
- if getattr(self.device, "virtual_chassis", None) is None:
754
+ if self.parent and self.bridge.parent != self.parent:
755
+ if getattr(self.parent, "virtual_chassis", None) is None:
668
756
  raise ValidationError(
669
757
  {
670
758
  "bridge": (
671
759
  f"The selected bridge interface ({self.bridge}) belongs to a different device "
672
- f"({self.bridge.device})."
760
+ f"({self.bridge.parent})."
673
761
  )
674
762
  }
675
763
  )
676
- elif self.bridge.device.virtual_chassis_id != self.device.virtual_chassis_id:
764
+ elif self.bridge.parent.virtual_chassis_id != self.parent.virtual_chassis_id:
677
765
  raise ValidationError(
678
766
  {
679
767
  "bridge": (
680
- f"The selected bridge interface ({self.bridge}) belongs to {self.bridge.device}, which "
681
- f"is not part of virtual chassis {self.device.virtual_chassis}."
768
+ f"The selected bridge interface ({self.bridge}) belongs to {self.bridge.parent}, which "
769
+ f"is not part of virtual chassis {self.parent.virtual_chassis}."
682
770
  )
683
771
  }
684
772
  )
@@ -766,10 +854,6 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
766
854
  def ip_address_count(self):
767
855
  return self.ip_addresses.count()
768
856
 
769
- @property
770
- def parent(self):
771
- return self.device
772
-
773
857
 
774
858
  @extras_features(
775
859
  "custom_fields",
@@ -879,6 +963,7 @@ class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
879
963
  related_name="interface_redundancy_group_associations",
880
964
  )
881
965
  priority = models.PositiveSmallIntegerField()
966
+ is_metadata_associable_model = False
882
967
 
883
968
  class Meta:
884
969
  """Meta class."""
@@ -888,7 +973,7 @@ class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
888
973
 
889
974
  def __str__(self):
890
975
  """Return a string representation of the instance."""
891
- return f"{self.interface_redundancy_group}: {self.interface.device} {self.interface}: {self.priority}"
976
+ return f"{self.interface_redundancy_group}: {self.interface.device or self.interface.module} {self.interface}: {self.priority}"
892
977
 
893
978
 
894
979
  #
@@ -897,9 +982,9 @@ class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
897
982
 
898
983
 
899
984
  @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
900
- class FrontPort(CableTermination, ComponentModel):
985
+ class FrontPort(ModularComponentModel, CableTermination):
901
986
  """
902
- A pass-through port on the front of a Device.
987
+ A pass-through port on the front of a Device or Module.
903
988
  """
904
989
 
905
990
  type = models.CharField(max_length=50, choices=PortTypeChoices)
@@ -912,22 +997,22 @@ class FrontPort(CableTermination, ComponentModel):
912
997
  ],
913
998
  )
914
999
 
915
- class Meta:
916
- ordering = ("device", "_name")
917
- unique_together = (
918
- ("device", "name"),
919
- ("rear_port", "rear_port_position"),
920
- )
1000
+ natural_key_field_names = ["device", "module", "name", "rear_port", "rear_port_position"]
921
1001
 
922
- @property
923
- def parent(self):
924
- return self.device
1002
+ class Meta(ModularComponentModel.Meta):
1003
+ constraints = [
1004
+ *ModularComponentModel.Meta.constraints,
1005
+ models.UniqueConstraint(
1006
+ fields=("rear_port", "rear_port_position"),
1007
+ name="dcim_frontport_rear_port_position_unique",
1008
+ ),
1009
+ ]
925
1010
 
926
1011
  def clean(self):
927
1012
  super().clean()
928
1013
 
929
1014
  # Validate rear port assignment
930
- if self.rear_port.device != self.device:
1015
+ if self.parent and self.rear_port.parent != self.parent:
931
1016
  raise ValidationError({"rear_port": f"Rear port ({self.rear_port}) must belong to the same device"})
932
1017
 
933
1018
  # Validate rear port position assignment
@@ -941,9 +1026,9 @@ class FrontPort(CableTermination, ComponentModel):
941
1026
 
942
1027
 
943
1028
  @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
944
- class RearPort(CableTermination, ComponentModel):
1029
+ class RearPort(ModularComponentModel, CableTermination):
945
1030
  """
946
- A pass-through port on the rear of a Device.
1031
+ A pass-through port on the rear of a Device or Module.
947
1032
  """
948
1033
 
949
1034
  type = models.CharField(max_length=50, choices=PortTypeChoices)
@@ -955,10 +1040,6 @@ class RearPort(CableTermination, ComponentModel):
955
1040
  ],
956
1041
  )
957
1042
 
958
- class Meta:
959
- ordering = ("device", "_name")
960
- unique_together = ("device", "name")
961
-
962
1043
  def clean(self):
963
1044
  super().clean()
964
1045
 
@@ -972,10 +1053,6 @@ class RearPort(CableTermination, ComponentModel):
972
1053
  }
973
1054
  )
974
1055
 
975
- @property
976
- def parent(self):
977
- return self.device
978
-
979
1056
 
980
1057
  #
981
1058
  # Device bays
@@ -1027,10 +1104,6 @@ class DeviceBay(ComponentModel):
1027
1104
  }
1028
1105
  )
1029
1106
 
1030
- @property
1031
- def parent(self):
1032
- return self.device
1033
-
1034
1107
 
1035
1108
  #
1036
1109
  # Inventory items
@@ -1079,7 +1152,8 @@ class InventoryItem(TreeModel, ComponentModel):
1079
1152
  related_name="inventory_items",
1080
1153
  blank=True,
1081
1154
  null=True,
1082
- verbose_name="The software version installed on this inventory item",
1155
+ verbose_name="Software Version",
1156
+ help_text="The software version installed on this inventory item",
1083
1157
  )
1084
1158
  software_image_files = models.ManyToManyField(
1085
1159
  to="dcim.SoftwareImageFile",
@@ -1101,3 +1175,105 @@ class InventoryItem(TreeModel, ComponentModel):
1101
1175
  For the time being we just use the PK as a natural key.
1102
1176
  """
1103
1177
  return ["pk"]
1178
+
1179
+
1180
+ @extras_features(
1181
+ "custom_links",
1182
+ "custom_validators",
1183
+ "export_templates",
1184
+ "graphql",
1185
+ "webhooks",
1186
+ )
1187
+ class ModuleBay(PrimaryModel):
1188
+ """
1189
+ A slot in a Device or Module which can contain Modules.
1190
+ """
1191
+
1192
+ parent_device = models.ForeignKey(
1193
+ to="dcim.Device",
1194
+ on_delete=models.CASCADE,
1195
+ related_name="module_bays",
1196
+ blank=True,
1197
+ null=True,
1198
+ )
1199
+ parent_module = models.ForeignKey(
1200
+ to="dcim.Module",
1201
+ on_delete=models.CASCADE,
1202
+ related_name="module_bays",
1203
+ blank=True,
1204
+ null=True,
1205
+ )
1206
+ name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, db_index=True)
1207
+ _name = NaturalOrderingField(target_field="name", max_length=CHARFIELD_MAX_LENGTH, blank=True, db_index=True)
1208
+ position = models.CharField(
1209
+ blank=True,
1210
+ max_length=CHARFIELD_MAX_LENGTH,
1211
+ help_text="The position of the module bay within the parent device/module",
1212
+ )
1213
+ label = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True, help_text="Physical label")
1214
+ description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
1215
+
1216
+ clone_fields = ["parent_device", "parent_module"]
1217
+
1218
+ # The recursive nature of this model combined with the fact that it can be a child of a
1219
+ # device or location makes our natural key implementation unusable, so just use the pk
1220
+ natural_key_field_names = ["pk"]
1221
+
1222
+ class Meta:
1223
+ # TODO: Ordering by parent_module.id is not correct but prevents an infinite loop
1224
+ ordering = (
1225
+ "parent_device",
1226
+ "parent_module__id",
1227
+ "_name",
1228
+ )
1229
+ constraints = [
1230
+ models.UniqueConstraint(
1231
+ fields=["parent_device", "name"],
1232
+ name="dcim_modulebay_parent_device_name_unique",
1233
+ ),
1234
+ models.UniqueConstraint(
1235
+ fields=["parent_module", "name"],
1236
+ name="dcim_modulebay_parent_module_name_unique",
1237
+ ),
1238
+ ]
1239
+
1240
+ @property
1241
+ def parent(self):
1242
+ """Walk up parent chain to find the Device that this ModuleBay is installed in, if one exists."""
1243
+ return self.parent_module.device if self.parent_module else self.parent_device
1244
+
1245
+ def __str__(self):
1246
+ if self.parent_device is not None:
1247
+ return f"{self.parent_device} ({self.name})"
1248
+ else:
1249
+ return f"{self.parent_module} ({self.name})"
1250
+
1251
+ @property
1252
+ def display(self):
1253
+ if self.parent_device is not None:
1254
+ return f"{self.parent_device.display} ({self.name})"
1255
+ else:
1256
+ return f"{self.parent_module.display} ({self.name})"
1257
+
1258
+ def to_objectchange(self, action, **kwargs):
1259
+ """
1260
+ Return a new ObjectChange with the `related_object` pinned to the parent `device` or `module`.
1261
+ """
1262
+ # Annotate the parent
1263
+ try:
1264
+ parent = self.parent
1265
+ except ObjectDoesNotExist:
1266
+ # The parent has already been deleted
1267
+ parent = None
1268
+
1269
+ return super().to_objectchange(action, related_object=parent, **kwargs)
1270
+
1271
+ def clean(self):
1272
+ super().clean()
1273
+
1274
+ # Validate that a Device or Module is set, but not both
1275
+ if self.parent_device and self.parent_module:
1276
+ raise ValidationError("Only one of parent_device or parent_module must be set")
1277
+
1278
+ if not (self.parent_device or self.parent_module):
1279
+ raise ValidationError("Either parent_device or parent_module must be set")