nautobot 3.0.0a2__py3-none-any.whl → 3.0.0a3__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 (420) hide show
  1. nautobot/apps/choices.py +0 -2
  2. nautobot/apps/filters.py +7 -9
  3. nautobot/apps/models.py +2 -2
  4. nautobot/apps/ui.py +9 -1
  5. nautobot/circuits/filters.py +3 -2
  6. nautobot/circuits/navigation.py +3 -2
  7. nautobot/circuits/templates/circuits/circuit.html +1 -1
  8. nautobot/circuits/templates/circuits/circuit_create.html +3 -3
  9. nautobot/circuits/templates/circuits/circuittermination.html +1 -1
  10. nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
  11. nautobot/circuits/templates/circuits/circuittype.html +1 -1
  12. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
  13. nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
  14. nautobot/circuits/templates/circuits/providernetwork.html +1 -1
  15. nautobot/circuits/tests/integration/test_circuit.py +10 -13
  16. nautobot/cloud/filters.py +1 -1
  17. nautobot/cloud/navigation.py +3 -2
  18. nautobot/core/api/schema.py +1 -1
  19. nautobot/core/api/serializers.py +6 -1
  20. nautobot/core/api/urls.py +1 -0
  21. nautobot/core/api/views.py +8 -0
  22. nautobot/core/apps/__init__.py +11 -10
  23. nautobot/core/celery/__init__.py +3 -5
  24. nautobot/core/checks.py +46 -0
  25. nautobot/core/cli/bootstrap_v3_to_v5.py +70 -1
  26. nautobot/core/cli/migrate_deprecated_templates.py +200 -0
  27. nautobot/core/constants.py +3 -0
  28. nautobot/core/context_processors.py +9 -1
  29. nautobot/core/forms/forms.py +1 -1
  30. nautobot/core/jobs/__init__.py +6 -3
  31. nautobot/core/jobs/groups.py +31 -1
  32. nautobot/core/management/commands/generate_test_data.py +28 -9
  33. nautobot/core/models/generics.py +9 -1
  34. nautobot/core/models/tree_queries.py +10 -5
  35. nautobot/core/settings.py +18 -12
  36. nautobot/core/settings.yaml +13 -7
  37. nautobot/core/signals.py +12 -1
  38. nautobot/core/tables.py +13 -6
  39. nautobot/core/templates/40x.html +1 -1
  40. nautobot/core/templates/500.html +2 -2
  41. nautobot/core/templates/admin/config/config.html +12 -12
  42. nautobot/core/templates/admin/index.html +3 -3
  43. nautobot/core/templates/buttons/export.html +1 -1
  44. nautobot/core/templates/components/button/dropdown.html +5 -3
  45. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  46. nautobot/core/templates/components/panel/panel.html +3 -3
  47. nautobot/core/templates/components/tab/content_wrapper.html +2 -3
  48. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
  49. nautobot/core/templates/echarts/echarts.html +1 -1
  50. nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
  51. nautobot/core/templates/generic/object_bulk_create.html +4 -3
  52. nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
  53. nautobot/core/templates/generic/object_bulk_remove.html +2 -2
  54. nautobot/core/templates/generic/object_bulk_update.html +5 -4
  55. nautobot/core/templates/generic/object_create.html +5 -4
  56. nautobot/core/templates/generic/object_import.html +2 -1
  57. nautobot/core/templates/generic/object_list.html +12 -4
  58. nautobot/core/templates/generic/object_notes.html +5 -3
  59. nautobot/core/templates/generic/object_retrieve.html +2 -3
  60. nautobot/core/templates/graphene/graphiql.html +7 -7
  61. nautobot/core/templates/home.html +1 -1
  62. nautobot/core/templates/import_success.html +2 -1
  63. nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
  64. nautobot/core/templates/inc/created_updated.html +7 -3
  65. nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
  66. nautobot/core/templates/inc/form_static_field.html +6 -0
  67. nautobot/core/templates/inc/header.html +1 -1
  68. nautobot/core/templates/inc/image_attachments.html +2 -1
  69. nautobot/core/templates/inc/nav_menu.html +2 -1
  70. nautobot/core/templates/inc/search_panel.html +4 -4
  71. nautobot/core/templates/login.html +4 -2
  72. nautobot/core/templates/nautobot_config.py.j2 +6 -5
  73. nautobot/core/templates/redoc_ui.html +7 -0
  74. nautobot/core/templates/search.html +1 -1
  75. nautobot/core/templates/swagger_ui.html +17 -3
  76. nautobot/core/templates/system_jobs/import_objects.html +1 -2
  77. nautobot/core/templates/utilities/confirmation_form.html +2 -2
  78. nautobot/core/templates/utilities/obj_table.html +10 -2
  79. nautobot/core/templates/utilities/render_field.html +7 -7
  80. nautobot/core/templates/utilities/render_jinja2.html +2 -2
  81. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +4 -4
  82. nautobot/core/templates/utilities/theme_preview.html +16 -3
  83. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  84. nautobot/core/templatetags/helpers.py +52 -6
  85. nautobot/core/testing/api.py +68 -9
  86. nautobot/core/testing/filters.py +0 -23
  87. nautobot/core/testing/integration.py +23 -10
  88. nautobot/core/testing/mixins.py +2 -0
  89. nautobot/core/testing/views.py +4 -0
  90. nautobot/core/tests/integration/test_app_home.py +34 -30
  91. nautobot/core/tests/integration/test_app_navbar.py +3 -0
  92. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  93. nautobot/core/tests/runner.py +9 -1
  94. nautobot/core/tests/test_api.py +5 -3
  95. nautobot/core/tests/test_breadcrumbs.py +6 -7
  96. nautobot/core/tests/test_checks.py +28 -0
  97. nautobot/core/tests/test_cli.py +40 -0
  98. nautobot/core/tests/test_config.py +2 -1
  99. nautobot/core/tests/test_forms.py +55 -13
  100. nautobot/core/tests/test_jobs.py +75 -1
  101. nautobot/core/tests/test_nautobot_server.py +2 -0
  102. nautobot/core/tests/test_navigations.py +76 -1
  103. nautobot/core/tests/test_patch_social_django.py +42 -0
  104. nautobot/core/tests/test_tables.py +3 -1
  105. nautobot/core/tests/test_templatetags_helpers.py +53 -13
  106. nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
  107. nautobot/core/tests/test_tree_queries.py +14 -1
  108. nautobot/core/tests/test_ui.py +1 -1
  109. nautobot/core/tests/test_utils.py +31 -4
  110. nautobot/core/tests/test_views.py +159 -31
  111. nautobot/core/ui/breadcrumbs.py +2 -12
  112. nautobot/core/ui/choices.py +142 -10
  113. nautobot/core/ui/constants.py +76 -12
  114. nautobot/core/ui/object_detail.py +92 -12
  115. nautobot/core/urls.py +12 -1
  116. nautobot/core/utils/cache.py +2 -1
  117. nautobot/core/utils/filtering.py +17 -17
  118. nautobot/core/utils/lookup.py +3 -8
  119. nautobot/core/utils/module_loading.py +21 -0
  120. nautobot/core/utils/patch_social_django.py +128 -0
  121. nautobot/core/views/__init__.py +38 -1
  122. nautobot/core/views/generic.py +3 -3
  123. nautobot/core/views/mixins.py +15 -3
  124. nautobot/core/views/renderers.py +2 -0
  125. nautobot/core/views/viewsets.py +2 -1
  126. nautobot/data_validation/apps.py +1 -5
  127. nautobot/data_validation/custom_validators.py +4 -4
  128. nautobot/data_validation/filters.py +1 -1
  129. nautobot/data_validation/forms.py +40 -0
  130. nautobot/data_validation/migrations/0001_initial.py +0 -7
  131. nautobot/data_validation/migrations/0002_data_migration_from_app.py +0 -12
  132. nautobot/data_validation/models.py +16 -7
  133. nautobot/data_validation/navigation.py +8 -1
  134. nautobot/data_validation/tables.py +12 -5
  135. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
  136. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  137. nautobot/data_validation/tests/__init__.py +2 -2
  138. nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
  139. nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
  140. nautobot/data_validation/tests/test_filters.py +8 -6
  141. nautobot/data_validation/tests/test_models.py +15 -0
  142. nautobot/data_validation/tests/test_views.py +190 -32
  143. nautobot/data_validation/urls.py +2 -5
  144. nautobot/data_validation/views.py +73 -40
  145. nautobot/dcim/api/serializers.py +0 -13
  146. nautobot/dcim/apps.py +4 -0
  147. nautobot/dcim/choices.py +16 -0
  148. nautobot/dcim/custom_validators.py +84 -0
  149. nautobot/dcim/filter_mixins.py +353 -4
  150. nautobot/dcim/{filters/__init__.py → filters.py} +2 -35
  151. nautobot/dcim/forms.py +1 -1
  152. nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
  153. nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
  154. nautobot/dcim/models/device_components.py +81 -68
  155. nautobot/dcim/models/devices.py +13 -16
  156. nautobot/dcim/navigation.py +7 -6
  157. nautobot/dcim/tables/devices.py +3 -0
  158. nautobot/dcim/tables/template_code.py +14 -14
  159. nautobot/dcim/templates/dcim/cable.html +2 -61
  160. nautobot/dcim/templates/dcim/cable_connect.html +28 -112
  161. nautobot/dcim/templates/dcim/cable_edit.html +2 -5
  162. nautobot/dcim/templates/dcim/cable_retrieve.html +61 -0
  163. nautobot/dcim/templates/dcim/cable_trace.html +1 -3
  164. nautobot/dcim/templates/dcim/cable_update.html +5 -0
  165. nautobot/dcim/templates/dcim/consoleport.html +6 -5
  166. nautobot/dcim/templates/dcim/consoleserverport.html +6 -5
  167. nautobot/dcim/templates/dcim/device/config.html +2 -2
  168. nautobot/dcim/templates/dcim/device/consoleports.html +1 -1
  169. nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -1
  170. nautobot/dcim/templates/dcim/device/devicebays.html +1 -1
  171. nautobot/dcim/templates/dcim/device/frontports.html +1 -1
  172. nautobot/dcim/templates/dcim/device/interfaces.html +1 -1
  173. nautobot/dcim/templates/dcim/device/inventory.html +1 -1
  174. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
  175. nautobot/dcim/templates/dcim/device/modulebays.html +1 -1
  176. nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -1
  177. nautobot/dcim/templates/dcim/device/powerports.html +1 -1
  178. nautobot/dcim/templates/dcim/device/rearports.html +1 -1
  179. nautobot/dcim/templates/dcim/device/status.html +8 -8
  180. nautobot/dcim/templates/dcim/device/wireless.html +1 -1
  181. nautobot/dcim/templates/dcim/device.html +1 -1
  182. nautobot/dcim/templates/dcim/device_component_add.html +2 -2
  183. nautobot/dcim/templates/dcim/device_create.html +5 -3
  184. nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
  185. nautobot/dcim/templates/dcim/device_list.html +73 -10
  186. nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
  187. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  188. nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
  189. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
  190. nautobot/dcim/templates/dcim/frontport.html +9 -8
  191. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  192. nautobot/dcim/templates/dcim/interface.html +26 -6
  193. nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
  194. nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
  195. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  196. nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
  197. nautobot/dcim/templates/dcim/location_retrieve.html +1 -242
  198. nautobot/dcim/templates/dcim/module/base.html +49 -9
  199. nautobot/dcim/templates/dcim/module_list.html +57 -8
  200. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
  201. nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
  202. nautobot/dcim/templates/dcim/platform_create.html +1 -1
  203. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  204. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  205. nautobot/dcim/templates/dcim/powerport.html +5 -4
  206. nautobot/dcim/templates/dcim/rack_elevation_list.html +16 -4
  207. nautobot/dcim/templates/dcim/rack_retrieve.html +33 -15
  208. nautobot/dcim/templates/dcim/rearport.html +7 -6
  209. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  210. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
  211. nautobot/dcim/templates/dcim/virtualchassis_update.html +14 -6
  212. nautobot/dcim/tests/integration/test_controller.py +1 -0
  213. nautobot/dcim/tests/test_api.py +8 -0
  214. nautobot/dcim/tests/test_custom_validators.py +229 -0
  215. nautobot/dcim/tests/test_filters.py +12 -6
  216. nautobot/dcim/tests/test_models.py +63 -4
  217. nautobot/dcim/tests/test_views.py +63 -22
  218. nautobot/dcim/urls.py +64 -21
  219. nautobot/dcim/utils.py +3 -3
  220. nautobot/dcim/views.py +547 -273
  221. nautobot/extras/api/views.py +9 -1
  222. nautobot/extras/choices.py +2 -13
  223. nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
  224. nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
  225. nautobot/extras/{filters/__init__.py → filters.py} +14 -46
  226. nautobot/extras/forms/forms.py +5 -13
  227. nautobot/extras/forms/mixins.py +0 -41
  228. nautobot/extras/management/__init__.py +9 -0
  229. nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
  230. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  231. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  232. nautobot/extras/models/__init__.py +1 -2
  233. nautobot/extras/models/approvals.py +22 -13
  234. nautobot/extras/models/contacts.py +2 -0
  235. nautobot/extras/models/groups.py +44 -5
  236. nautobot/extras/models/jobs.py +59 -1
  237. nautobot/extras/models/mixins.py +28 -0
  238. nautobot/extras/models/models.py +13 -0
  239. nautobot/extras/models/secrets.py +1 -0
  240. nautobot/extras/models/statuses.py +0 -15
  241. nautobot/extras/navigation.py +13 -9
  242. nautobot/extras/plugins/__init__.py +33 -55
  243. nautobot/extras/plugins/tables.py +3 -3
  244. nautobot/extras/plugins/urls.py +2 -21
  245. nautobot/extras/plugins/utils.py +1 -33
  246. nautobot/extras/plugins/views.py +0 -4
  247. nautobot/extras/signals.py +20 -19
  248. nautobot/extras/tables.py +52 -68
  249. nautobot/extras/templates/extras/approval_dashboard.html +7 -5
  250. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
  251. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
  252. nautobot/extras/templates/extras/computedfield.html +1 -1
  253. nautobot/extras/templates/extras/configcontext.html +1 -1
  254. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  255. nautobot/extras/templates/extras/customfield.html +1 -1
  256. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
  257. nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
  258. nautobot/extras/templates/extras/gitrepository_result.html +0 -2
  259. nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -96
  260. nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
  261. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
  262. nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
  263. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
  264. nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
  265. nautobot/extras/templates/extras/inc/json_format.html +10 -3
  266. nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
  267. nautobot/extras/templates/extras/job.html +29 -12
  268. nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
  269. nautobot/extras/templates/extras/job_edit.html +52 -46
  270. nautobot/extras/templates/extras/job_list.html +29 -25
  271. nautobot/extras/templates/extras/marketplace.html +5 -9
  272. nautobot/extras/templates/extras/object_configcontext.html +1 -1
  273. nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
  274. nautobot/extras/templates/extras/objectchange_retrieve.html +19 -37
  275. nautobot/extras/templates/extras/plugin_detail.html +26 -21
  276. nautobot/extras/templates/extras/plugins_list.html +16 -26
  277. nautobot/extras/templates/extras/role_retrieve.html +64 -0
  278. nautobot/extras/templates/extras/scheduledjob.html +4 -2
  279. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  280. nautobot/extras/templates/extras/tag.html +1 -1
  281. nautobot/extras/templatetags/custom_links.py +12 -12
  282. nautobot/extras/templatetags/job_buttons.py +14 -12
  283. nautobot/extras/test_jobs/invalid_import.py +9 -0
  284. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  285. nautobot/extras/test_jobs/missing_import.py +11 -0
  286. nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
  287. nautobot/extras/tests/integration/test_customfields.py +8 -7
  288. nautobot/extras/tests/integration/test_dynamicgroups.py +5 -1
  289. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  290. nautobot/extras/tests/integration/test_plugins.py +18 -6
  291. nautobot/extras/tests/test_api.py +27 -18
  292. nautobot/extras/tests/test_approvals.py +38 -38
  293. nautobot/extras/tests/test_changelog.py +35 -3
  294. nautobot/extras/tests/test_customfields.py +22 -13
  295. nautobot/extras/tests/test_customfields_filters.py +479 -0
  296. nautobot/extras/tests/test_dynamicgroups.py +39 -1
  297. nautobot/extras/tests/test_filters.py +21 -19
  298. nautobot/extras/tests/test_forms.py +18 -21
  299. nautobot/extras/tests/test_jobs.py +25 -4
  300. nautobot/extras/tests/test_migrations.py +1 -0
  301. nautobot/extras/tests/test_models.py +13 -31
  302. nautobot/extras/tests/test_plugins.py +36 -10
  303. nautobot/extras/tests/test_views.py +31 -30
  304. nautobot/extras/views.py +81 -19
  305. nautobot/ipam/factory.py +7 -0
  306. nautobot/ipam/filter_mixins.py +38 -0
  307. nautobot/ipam/filters.py +27 -38
  308. nautobot/ipam/formfields.py +1 -1
  309. nautobot/ipam/forms.py +6 -3
  310. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  311. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  312. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  313. nautobot/ipam/models.py +29 -2
  314. nautobot/ipam/navigation.py +3 -2
  315. nautobot/ipam/signals.py +71 -0
  316. nautobot/ipam/tables.py +13 -6
  317. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  318. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
  319. nautobot/ipam/templates/ipam/ipaddress.html +14 -0
  320. nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
  321. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
  322. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  323. nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
  324. nautobot/ipam/templates/ipam/prefix_list.html +14 -13
  325. nautobot/ipam/templates/ipam/service.html +1 -1
  326. nautobot/ipam/templates/ipam/vlan.html +1 -1
  327. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  328. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  329. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  330. nautobot/ipam/tests/test_api.py +13 -6
  331. nautobot/ipam/tests/test_filters.py +10 -0
  332. nautobot/ipam/tests/test_forms.py +1 -1
  333. nautobot/ipam/tests/test_models.py +43 -1
  334. nautobot/ipam/tests/test_tables.py +1 -2
  335. nautobot/ipam/tests/test_utils.py +1 -1
  336. nautobot/ipam/tests/test_views.py +13 -14
  337. nautobot/ipam/ui.py +0 -17
  338. nautobot/ipam/utils/migrations.py +16 -2
  339. nautobot/ipam/utils/testing.py +9 -3
  340. nautobot/ipam/views.py +46 -6
  341. nautobot/project-static/dist/css/nautobot.css +1 -1
  342. nautobot/project-static/dist/css/nautobot.css.map +1 -1
  343. nautobot/project-static/dist/js/nautobot.js +1 -1
  344. nautobot/project-static/dist/js/nautobot.js.map +1 -1
  345. nautobot/project-static/js/cabletrace.js +1 -1
  346. nautobot/project-static/js/interface_filtering.js +20 -16
  347. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  348. nautobot/project-static/nautobot-icons/cloud.svg +1 -1
  349. nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
  350. nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
  351. nautobot/project-static/nautobot-icons/elements.svg +1 -1
  352. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  353. nautobot/project-static/nautobot-icons/hammer.svg +1 -1
  354. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  355. nautobot/project-static/nautobot-icons/secrets.svg +1 -1
  356. nautobot/project-static/nautobot-icons/security.svg +3 -0
  357. nautobot/project-static/nautobot-icons/server.svg +1 -1
  358. nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
  359. nautobot/project-static/nautobot-icons/star.svg +1 -1
  360. nautobot/tenancy/api/serializers.py +1 -0
  361. nautobot/tenancy/api/views.py +2 -1
  362. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  363. nautobot/tenancy/navigation.py +3 -1
  364. nautobot/tenancy/tests/test_filters.py +0 -2
  365. nautobot/tenancy/views.py +2 -1
  366. nautobot/ui/src/js/collapse.js +3 -3
  367. nautobot/ui/src/js/nautobot.js +16 -0
  368. nautobot/ui/src/scss/colors.scss +1 -1
  369. nautobot/ui/src/scss/nautobot.scss +61 -28
  370. nautobot/users/templates/users/profile.html +45 -12
  371. nautobot/users/templates/users/sessionkey_delete.html +1 -1
  372. nautobot/users/tests/test_api.py +4 -0
  373. nautobot/users/views.py +4 -2
  374. nautobot/virtualization/models.py +1 -68
  375. nautobot/virtualization/navigation.py +3 -2
  376. nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
  377. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  378. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
  379. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
  380. nautobot/virtualization/tests/test_api.py +3 -0
  381. nautobot/virtualization/tests/test_models.py +44 -4
  382. nautobot/vpn/__init__.py +0 -0
  383. nautobot/vpn/api/serializers.py +113 -0
  384. nautobot/vpn/api/urls.py +19 -0
  385. nautobot/vpn/api/views.py +70 -0
  386. nautobot/vpn/apps.py +8 -0
  387. nautobot/vpn/choices.py +171 -0
  388. nautobot/vpn/factory.py +209 -0
  389. nautobot/vpn/filters.py +233 -0
  390. nautobot/vpn/forms.py +486 -0
  391. nautobot/vpn/homepage.py +19 -0
  392. nautobot/vpn/migrations/0001_initial.py +541 -0
  393. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  394. nautobot/vpn/migrations/__init__.py +0 -0
  395. nautobot/vpn/models.py +527 -0
  396. nautobot/vpn/navigation.py +98 -0
  397. nautobot/vpn/tables.py +380 -0
  398. nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
  399. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  400. nautobot/vpn/tests/__init__.py +0 -0
  401. nautobot/vpn/tests/test_api.py +341 -0
  402. nautobot/vpn/tests/test_filters.py +139 -0
  403. nautobot/vpn/tests/test_forms.py +294 -0
  404. nautobot/vpn/tests/test_models.py +97 -0
  405. nautobot/vpn/tests/test_views.py +281 -0
  406. nautobot/vpn/urls.py +16 -0
  407. nautobot/vpn/views.py +437 -0
  408. nautobot/wireless/navigation.py +3 -2
  409. nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
  410. nautobot/wireless/tests/test_api.py +1 -1
  411. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +14 -14
  412. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +417 -366
  413. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
  414. nautobot/data_validation/template_content.py +0 -42
  415. nautobot/dcim/filters/mixins.py +0 -354
  416. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  417. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  418. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
  419. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
  420. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
@@ -154,6 +154,11 @@ class VMInterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase
154
154
  self.assertEqual(count, 1)
155
155
  self.assertEqual(IPAddressToInterface.objects.filter(ip_address=ips[-1], vm_interface=vm_interface).count(), 1)
156
156
 
157
+ # add a single instance which is already there
158
+ count = vm_interface.add_ip_addresses(ips[-1])
159
+ self.assertEqual(count, 0)
160
+ self.assertEqual(IPAddressToInterface.objects.filter(ip_address=ips[-1], vm_interface=vm_interface).count(), 1)
161
+
157
162
  # add multiple instances
158
163
  count = vm_interface.add_ip_addresses(ips[:5])
159
164
  self.assertEqual(count, 5)
@@ -161,12 +166,27 @@ class VMInterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase
161
166
  for ip in ips[:5]:
162
167
  self.assertEqual(IPAddressToInterface.objects.filter(ip_address=ip, vm_interface=vm_interface).count(), 1)
163
168
 
169
+ # add multiple instances all of which are already there
170
+ count = vm_interface.add_ip_addresses(ips[:5])
171
+ self.assertEqual(count, 0)
172
+ self.assertEqual(IPAddressToInterface.objects.filter(vm_interface=vm_interface).count(), 6)
173
+ for ip in ips[:5]:
174
+ self.assertEqual(IPAddressToInterface.objects.filter(ip_address=ip, vm_interface=vm_interface).count(), 1)
175
+
176
+ # add multiple IPs some of which are there
177
+ count = vm_interface.add_ip_addresses(ips[3:7])
178
+ self.assertEqual(count, 2)
179
+ self.assertEqual(IPAddressToInterface.objects.filter(vm_interface=vm_interface).count(), 8)
180
+ for ip in ips[3:7]:
181
+ self.assertEqual(IPAddressToInterface.objects.filter(ip_address=ip, vm_interface=vm_interface).count(), 1)
182
+
164
183
  def test_remove_ip_addresses(self):
165
184
  """Test the `remove_ip_addresses` helper method on `VMInterface`"""
166
185
  vm_interface = VMInterface.objects.create(
167
186
  name="Int1", virtual_machine=self.virtualmachine, status=self.int_status
168
187
  )
169
- ips = list(IPAddress.objects.all()[:10])
188
+ ips = list(IPAddress.objects.filter(ip_version=4)[:10])
189
+ self.assertEqual(len(ips), 10)
170
190
 
171
191
  # baseline (no vm_interface to ip address relationships exists)
172
192
  self.assertFalse(IPAddressToInterface.objects.filter(vm_interface=vm_interface).exists())
@@ -179,13 +199,28 @@ class VMInterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase
179
199
  self.assertEqual(count, 1)
180
200
  self.assertEqual(IPAddressToInterface.objects.filter(vm_interface=vm_interface).count(), 9)
181
201
 
202
+ # remove a single instance which has already been removed
203
+ count = vm_interface.remove_ip_addresses(ips[-1])
204
+ self.assertEqual(count, 0)
205
+ self.assertEqual(IPAddressToInterface.objects.filter(vm_interface=vm_interface).count(), 9)
206
+
182
207
  # remove multiple instances
183
208
  count = vm_interface.remove_ip_addresses(ips[:5])
184
209
  self.assertEqual(count, 5)
185
210
  self.assertEqual(IPAddressToInterface.objects.filter(vm_interface=vm_interface).count(), 4)
186
211
 
212
+ # remove multiple instances all which have already been removed
213
+ count = vm_interface.remove_ip_addresses(ips[:5])
214
+ self.assertEqual(count, 0)
215
+ self.assertEqual(IPAddressToInterface.objects.filter(vm_interface=vm_interface).count(), 4)
216
+
217
+ # remove multiple instances some of which have already been removed
218
+ count = vm_interface.remove_ip_addresses(ips[3:7])
219
+ self.assertEqual(count, 2)
220
+ self.assertEqual(IPAddressToInterface.objects.filter(vm_interface=vm_interface).count(), 2)
221
+
187
222
  count = vm_interface.remove_ip_addresses(ips)
188
- self.assertEqual(count, 4)
223
+ self.assertEqual(count, 2)
189
224
  self.assertEqual(IPAddressToInterface.objects.filter(vm_interface=vm_interface).count(), 0)
190
225
 
191
226
  # Test the pre_delete signal for IPAddressToInterface instances
@@ -194,9 +229,14 @@ class VMInterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase
194
229
  self.virtualmachine.primary_ip6 = vm_interface.ip_addresses.all().filter(ip_version=6).first()
195
230
  self.virtualmachine.save()
196
231
 
197
- vm_interface.remove_ip_addresses(self.virtualmachine.primary_ip4)
232
+ count = vm_interface.remove_ip_addresses(self.virtualmachine.primary_ip4)
233
+ self.assertEqual(count, 1)
198
234
  self.virtualmachine.refresh_from_db()
199
235
  self.assertEqual(self.virtualmachine.primary_ip4, None)
200
- vm_interface.remove_ip_addresses(self.virtualmachine.primary_ip6)
236
+ # NOTE: This effectively tests what happens when you pass remove_ip_addresses None; it
237
+ # NOTE: does not remove a v6 address, because there are no v6 IPs used in this test
238
+ # NOTE: class.
239
+ count = vm_interface.remove_ip_addresses(self.virtualmachine.primary_ip6)
240
+ self.assertEqual(count, 0)
201
241
  self.virtualmachine.refresh_from_db()
202
242
  self.assertEqual(self.virtualmachine.primary_ip6, None)
File without changes
@@ -0,0 +1,113 @@
1
+ from rest_framework.serializers import ChoiceField, ListField
2
+
3
+ from nautobot.apps.api import NautobotModelSerializer, TaggedModelSerializerMixin
4
+
5
+ from .. import choices, models
6
+
7
+
8
+ class VPNProfileSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
9
+ """VPNProfile Serializer."""
10
+
11
+ class Meta:
12
+ """Meta attributes."""
13
+
14
+ model = models.VPNProfile
15
+ fields = "__all__"
16
+
17
+
18
+ class VPNPhase1PolicySerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
19
+ """VPNPhase1Policy Serializer."""
20
+
21
+ encryption_algorithm = ListField(
22
+ child=ChoiceField(choices=choices.EncryptionAlgorithmChoices),
23
+ allow_empty=True,
24
+ required=False,
25
+ )
26
+ integrity_algorithm = ListField(
27
+ child=ChoiceField(choices=choices.IntegrityAlgorithmChoices),
28
+ allow_empty=True,
29
+ required=False,
30
+ )
31
+ dh_group = ListField(
32
+ child=ChoiceField(choices=choices.DhGroupChoices),
33
+ allow_empty=True,
34
+ required=False,
35
+ )
36
+
37
+ class Meta:
38
+ """Meta attributes."""
39
+
40
+ model = models.VPNPhase1Policy
41
+ fields = "__all__"
42
+
43
+
44
+ class VPNPhase2PolicySerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
45
+ """VPNPhase2Policy Serializer."""
46
+
47
+ encryption_algorithm = ListField(
48
+ child=ChoiceField(choices=choices.EncryptionAlgorithmChoices),
49
+ allow_empty=True,
50
+ required=False,
51
+ )
52
+ integrity_algorithm = ListField(
53
+ child=ChoiceField(choices=choices.IntegrityAlgorithmChoices),
54
+ allow_empty=True,
55
+ required=False,
56
+ )
57
+ pfs_group = ListField(
58
+ child=ChoiceField(choices=choices.DhGroupChoices),
59
+ allow_empty=True,
60
+ required=False,
61
+ )
62
+
63
+ class Meta:
64
+ """Meta attributes."""
65
+
66
+ model = models.VPNPhase2Policy
67
+ fields = "__all__"
68
+
69
+
70
+ class VPNProfilePhase1PolicyAssignmentSerializer(NautobotModelSerializer):
71
+ """Serializer for `VPNProfilePhase1PolicyAssignment` objects."""
72
+
73
+ class Meta:
74
+ model = models.VPNProfilePhase1PolicyAssignment
75
+ fields = "__all__"
76
+
77
+
78
+ class VPNProfilePhase2PolicyAssignmentSerializer(NautobotModelSerializer):
79
+ """Serializer for `VPNProfilePhase2PolicyAssignment` objects."""
80
+
81
+ class Meta:
82
+ model = models.VPNProfilePhase2PolicyAssignment
83
+ fields = "__all__"
84
+
85
+
86
+ class VPNSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
87
+ """VPN Serializer."""
88
+
89
+ class Meta:
90
+ """Meta attributes."""
91
+
92
+ model = models.VPN
93
+ fields = "__all__"
94
+
95
+
96
+ class VPNTunnelSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
97
+ """VPNTunnel Serializer."""
98
+
99
+ class Meta:
100
+ """Meta attributes."""
101
+
102
+ model = models.VPNTunnel
103
+ fields = "__all__"
104
+
105
+
106
+ class VPNTunnelEndpointSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
107
+ """VPNTunnelEndpoint Serializer."""
108
+
109
+ class Meta:
110
+ """Meta attributes."""
111
+
112
+ model = models.VPNTunnelEndpoint
113
+ fields = "__all__"
@@ -0,0 +1,19 @@
1
+ """Django API urlpatterns declaration for the vpn models."""
2
+
3
+ from nautobot.apps.api import OrderedDefaultRouter
4
+
5
+ from . import views
6
+
7
+ app_name = "vpn-api"
8
+ router = OrderedDefaultRouter()
9
+ # add the name of your api endpoint, usually hyphenated model name in plural, e.g. "my-model-classes"
10
+ router.register("vpn-profiles", views.VPNProfileViewSet)
11
+ router.register("vpn-phase-1-policies", views.VPNPhase1PolicyViewSet)
12
+ router.register("vpn-phase-2-policies", views.VPNPhase2PolicyViewSet)
13
+ router.register("vpn-profile-phase-1-policy-assignments", views.VPNProfilePhase1PolicyAssignmentViewSet)
14
+ router.register("vpn-profile-phase-2-policy-assignments", views.VPNProfilePhase2PolicyAssignmentViewSet)
15
+ router.register("vpns", views.VPNViewSet)
16
+ router.register("vpn-tunnels", views.VPNTunnelViewSet)
17
+ router.register("vpn-tunnel-endpoints", views.VPNTunnelEndpointViewSet)
18
+
19
+ urlpatterns = router.urls
@@ -0,0 +1,70 @@
1
+ """API views for the vpn models."""
2
+
3
+ from nautobot.apps.api import ModelViewSet, NautobotModelViewSet
4
+
5
+ from .. import filters, models
6
+ from ..api import serializers
7
+
8
+
9
+ class VPNProfileViewSet(NautobotModelViewSet): # pylint: disable=too-many-ancestors
10
+ """VPNProfile viewset."""
11
+
12
+ queryset = models.VPNProfile.objects.all()
13
+ serializer_class = serializers.VPNProfileSerializer
14
+ filterset_class = filters.VPNProfileFilterSet
15
+
16
+
17
+ class VPNPhase1PolicyViewSet(NautobotModelViewSet): # pylint: disable=too-many-ancestors
18
+ """VPNPhase1Policy viewset."""
19
+
20
+ queryset = models.VPNPhase1Policy.objects.all()
21
+ serializer_class = serializers.VPNPhase1PolicySerializer
22
+ filterset_class = filters.VPNPhase1PolicyFilterSet
23
+
24
+
25
+ class VPNPhase2PolicyViewSet(NautobotModelViewSet): # pylint: disable=too-many-ancestors
26
+ """VPNPhase2Policy viewset."""
27
+
28
+ queryset = models.VPNPhase2Policy.objects.all()
29
+ serializer_class = serializers.VPNPhase2PolicySerializer
30
+ filterset_class = filters.VPNPhase2PolicyFilterSet
31
+
32
+
33
+ class VPNProfilePhase1PolicyAssignmentViewSet(ModelViewSet): # pylint: disable=too-many-ancestors
34
+ """VPNProfilePhase1PolicyAssignment viewset."""
35
+
36
+ queryset = models.VPNProfilePhase1PolicyAssignment.objects.all()
37
+ serializer_class = serializers.VPNProfilePhase1PolicyAssignmentSerializer
38
+ filterset_class = filters.VPNProfilePhase1PolicyAssignmentFilterSet
39
+
40
+
41
+ class VPNProfilePhase2PolicyAssignmentViewSet(ModelViewSet): # pylint: disable=too-many-ancestors
42
+ """VPNProfilePhase2PolicyAssignment viewset."""
43
+
44
+ queryset = models.VPNProfilePhase2PolicyAssignment.objects.all()
45
+ serializer_class = serializers.VPNProfilePhase2PolicyAssignmentSerializer
46
+ filterset_class = filters.VPNProfilePhase2PolicyAssignmentFilterSet
47
+
48
+
49
+ class VPNViewSet(NautobotModelViewSet): # pylint: disable=too-many-ancestors
50
+ """VPN viewset."""
51
+
52
+ queryset = models.VPN.objects.all()
53
+ serializer_class = serializers.VPNSerializer
54
+ filterset_class = filters.VPNFilterSet
55
+
56
+
57
+ class VPNTunnelViewSet(NautobotModelViewSet): # pylint: disable=too-many-ancestors
58
+ """VPNTunnel viewset."""
59
+
60
+ queryset = models.VPNTunnel.objects.all()
61
+ serializer_class = serializers.VPNTunnelSerializer
62
+ filterset_class = filters.VPNTunnelFilterSet
63
+
64
+
65
+ class VPNTunnelEndpointViewSet(NautobotModelViewSet): # pylint: disable=too-many-ancestors
66
+ """VPNTunnelEndpoint viewset."""
67
+
68
+ queryset = models.VPNTunnelEndpoint.objects.all()
69
+ serializer_class = serializers.VPNTunnelEndpointSerializer
70
+ filterset_class = filters.VPNTunnelEndpointFilterSet
nautobot/vpn/apps.py ADDED
@@ -0,0 +1,8 @@
1
+ from nautobot.core.apps import NautobotConfig
2
+
3
+
4
+ class VPNConfig(NautobotConfig):
5
+ default = True
6
+ name = "nautobot.vpn"
7
+ verbose_name = "VPNs"
8
+ searchable_models = []
@@ -0,0 +1,171 @@
1
+ """Custom choices for the vpn models."""
2
+
3
+ from nautobot.apps.choices import ChoiceSet
4
+
5
+
6
+ class VPNTunnelStatusChoices(ChoiceSet):
7
+ STATUS_ACTIVE = "active"
8
+ STATUS_DOWN = "down"
9
+ STATUS_DEPRECATED = "deprecated"
10
+
11
+ CHOICES = (
12
+ (STATUS_ACTIVE, "Active"),
13
+ (STATUS_DOWN, "Down"),
14
+ (STATUS_DEPRECATED, "Deprecated"),
15
+ )
16
+
17
+
18
+ class VPNTunnelEndpointRoleChoices(ChoiceSet):
19
+ ROLE_PEER = "peer"
20
+ ROLE_HUB = "hub"
21
+ ROLE_SPOKE = "spoke"
22
+
23
+ CHOICES = (
24
+ (ROLE_PEER, "Peer"),
25
+ (ROLE_HUB, "Hub"),
26
+ (ROLE_SPOKE, "Spoke"),
27
+ )
28
+
29
+
30
+ class IkeVersionChoices(ChoiceSet):
31
+ """Choices for the ike_version field on the VPNPhase1Policy model."""
32
+
33
+ ike_v1 = "IKEv1"
34
+ ike_v2 = "IKEv2"
35
+
36
+ CHOICES = (
37
+ (ike_v1, "IKEv1"),
38
+ (ike_v2, "IKEv2"),
39
+ )
40
+
41
+
42
+ class EncryptionAlgorithmChoices(ChoiceSet):
43
+ """Choices for the encryption_algorithm field on the VPNPhase1Policy model."""
44
+
45
+ aes_128_cbc = "AES-128-CBC"
46
+ aes_128_gcm = "AES-128-GCM"
47
+ aes_192_cbc = "AES-192-CBC"
48
+ aes_192_gcm = "AES-192-GCM"
49
+ aes_256_cbc = "AES-256-CBC"
50
+ aes_256_gcm = "AES-256-GCM"
51
+ des = "DES"
52
+ a3des = "3DES"
53
+
54
+ CHOICES = (
55
+ (aes_128_cbc, "AES-128-CBC"),
56
+ (aes_128_gcm, "AES-128-GCM"),
57
+ (aes_192_cbc, "AES-192-CBC"),
58
+ (aes_192_gcm, "AES-192-GCM"),
59
+ (aes_256_cbc, "AES-256-CBC"),
60
+ (aes_256_gcm, "AES-256-GCM"),
61
+ (des, "DES"),
62
+ (a3des, "3DES"),
63
+ )
64
+
65
+
66
+ class IntegrityAlgorithmChoices(ChoiceSet):
67
+ """Choices for the integrity_algorithm field on the VPNPhase1Policy model."""
68
+
69
+ md5 = "MD5"
70
+ sha1 = "SHA1"
71
+ sha256 = "SHA256"
72
+ sha384 = "SHA384"
73
+ sha512 = "SHA512"
74
+
75
+ CHOICES = (
76
+ (md5, "MD5"),
77
+ (sha1, "SHA1"),
78
+ (sha256, "SHA256"),
79
+ (sha384, "SHA384"),
80
+ (sha512, "SHA512"),
81
+ )
82
+
83
+
84
+ class DhGroupChoices(ChoiceSet):
85
+ """Choices for the dh_group field on the VPNPhase1Policy model."""
86
+
87
+ group1 = "1"
88
+ group2 = "2"
89
+ group5 = "5"
90
+ group14 = "14"
91
+ group19 = "19"
92
+ group20 = "20"
93
+ group21 = "21"
94
+ group22 = "22"
95
+ group23 = "23"
96
+ group24 = "24"
97
+ group25 = "25"
98
+ group26 = "26"
99
+ group27 = "27"
100
+ group28 = "28"
101
+ group29 = "29"
102
+ group30 = "30"
103
+ group31 = "31"
104
+ group32 = "32"
105
+ group33 = "33"
106
+ group34 = "34"
107
+
108
+ CHOICES = (
109
+ (group1, "Group 1"),
110
+ (group2, "Group 2"),
111
+ (group5, "Group 5"),
112
+ (group14, "Group 14"),
113
+ (group19, "Group 19"),
114
+ (group20, "Group 20"),
115
+ (group21, "Group 21"),
116
+ (group22, "Group 22"),
117
+ (group23, "Group 23"),
118
+ (group24, "Group 24"),
119
+ (group25, "Group 25"),
120
+ (group26, "Group 26"),
121
+ (group27, "Group 27"),
122
+ (group28, "Group 28"),
123
+ (group29, "Group 29"),
124
+ (group30, "Group 30"),
125
+ (group31, "Group 31"),
126
+ (group32, "Group 32"),
127
+ (group33, "Group 33"),
128
+ (group34, "Group 34"),
129
+ )
130
+
131
+
132
+ class AuthenticationMethodChoices(ChoiceSet):
133
+ """Choices for the authentication_method field on the VPNPhase1Policy model."""
134
+
135
+ psk = "PSK"
136
+ rsa = "RSA"
137
+ ecdsa = "ECDSA"
138
+ certificate = "Certificate"
139
+
140
+ CHOICES = (
141
+ (psk, "PSK"),
142
+ (rsa, "RSA"),
143
+ (ecdsa, "ECDSA"),
144
+ (certificate, "Certificate"),
145
+ )
146
+
147
+
148
+ class EncapsulationChoices(ChoiceSet):
149
+ """Choices for the encapsulation field on the VPNTunnel model."""
150
+
151
+ ipsec_transport = "IPsec-Transport"
152
+ ipsec_tunnel = "IPsec-Tunnel"
153
+ ip_in_ip = "IP-in-IP"
154
+ gre = "GRE"
155
+ wireguard = "WireGuard"
156
+ l2tp = "L2TP"
157
+ pptp = "PPTP"
158
+ openvpn = "OpenVPN"
159
+ eoip = "EoIP"
160
+
161
+ CHOICES = (
162
+ (ipsec_transport, "IPsec-Transport"),
163
+ (ipsec_tunnel, "IPsec-Tunnel"),
164
+ (ip_in_ip, "IP-in-IP"),
165
+ (gre, "GRE"),
166
+ (wireguard, "WireGuard"),
167
+ (l2tp, "L2TP"),
168
+ (pptp, "PPTP"),
169
+ (openvpn, "OpenVPN"),
170
+ (eoip, "EoIP"),
171
+ )
@@ -0,0 +1,209 @@
1
+ import factory
2
+ import faker
3
+
4
+ from nautobot.core.factory import (
5
+ get_random_instances,
6
+ NautobotBoolIterator,
7
+ PrimaryModelFactory,
8
+ random_instance,
9
+ UniqueFaker,
10
+ )
11
+ from nautobot.dcim.models import Device, Interface
12
+ from nautobot.extras.models import DynamicGroup, Role, SecretsGroup, Status
13
+ from nautobot.ipam.models import IPAddress, Prefix
14
+ from nautobot.tenancy.models import Tenant
15
+ from nautobot.vpn import choices, models
16
+
17
+
18
+ class VPNPhase1PolicyFactory(PrimaryModelFactory):
19
+ class Meta:
20
+ model = models.VPNPhase1Policy
21
+ exclude = ("has_description", "has_tenant")
22
+
23
+ name = UniqueFaker("word")
24
+ has_description = NautobotBoolIterator()
25
+ description = factory.Maybe("has_description", factory.Faker("sentence"), "")
26
+ ike_version = factory.Faker("random_element", elements=choices.IkeVersionChoices.values())
27
+ encryption_algorithm = factory.LazyFunction(
28
+ lambda: sorted(faker.Faker().random_elements(choices.EncryptionAlgorithmChoices.values(), unique=True))
29
+ )
30
+ integrity_algorithm = factory.LazyFunction(
31
+ lambda: sorted(faker.Faker().random_elements(choices.IntegrityAlgorithmChoices.values(), unique=True))
32
+ )
33
+ dh_group = factory.LazyFunction(
34
+ lambda: sorted(faker.Faker().random_elements(choices.DhGroupChoices.values(), unique=True))
35
+ )
36
+ lifetime_seconds = factory.Faker("pyint", min_value=0, max_value=3600, step=15)
37
+ lifetime_kb = factory.Faker("pyint", min_value=0, max_value=1024)
38
+ authentication_method = factory.Faker("random_element", elements=choices.AuthenticationMethodChoices.values())
39
+ has_tenant = NautobotBoolIterator()
40
+ tenant = factory.Maybe("has_tenant", random_instance(Tenant), None)
41
+
42
+
43
+ class VPNPhase2PolicyFactory(PrimaryModelFactory):
44
+ class Meta:
45
+ model = models.VPNPhase2Policy
46
+ exclude = ("has_description", "has_tenant")
47
+
48
+ name = UniqueFaker("word")
49
+ has_description = NautobotBoolIterator()
50
+ description = factory.Maybe("has_description", factory.Faker("sentence"), "")
51
+ encryption_algorithm = factory.LazyFunction(
52
+ lambda: sorted(faker.Faker().random_elements(choices.EncryptionAlgorithmChoices.values(), unique=True))
53
+ )
54
+ integrity_algorithm = factory.LazyFunction(
55
+ lambda: sorted(faker.Faker().random_elements(choices.IntegrityAlgorithmChoices.values(), unique=True))
56
+ )
57
+ pfs_group = factory.LazyFunction(
58
+ lambda: sorted(faker.Faker().random_elements(choices.DhGroupChoices.values(), unique=True))
59
+ )
60
+ lifetime = factory.Faker("pyint", min_value=0, max_value=3600, step=15)
61
+ has_tenant = NautobotBoolIterator()
62
+ tenant = factory.Maybe("has_tenant", random_instance(Tenant), None)
63
+
64
+
65
+ class VPNProfileFactory(PrimaryModelFactory):
66
+ class Meta:
67
+ model = models.VPNProfile
68
+ exclude = ("has_description", "has_role", "has_secrets_group", "has_extra_options", "has_tenant")
69
+
70
+ name = UniqueFaker("word")
71
+ has_description = NautobotBoolIterator()
72
+ description = factory.Maybe("has_description", factory.Faker("sentence"), "")
73
+ has_role = NautobotBoolIterator()
74
+ role = factory.Maybe(
75
+ "has_role",
76
+ random_instance(lambda: Role.objects.get_for_model(models.VPNProfile), allow_null=True),
77
+ None,
78
+ )
79
+ has_secrets_group = NautobotBoolIterator()
80
+ secrets_group = factory.Maybe(
81
+ "has_secrets_group",
82
+ random_instance(SecretsGroup),
83
+ )
84
+ keepalive_enabled = NautobotBoolIterator()
85
+ keepalive_interval = factory.Faker("pyint", min_value=0, max_value=3600, step=3)
86
+ keepalive_retries = factory.Faker("pyint", min_value=0, max_value=180, step=3)
87
+ nat_traversal = NautobotBoolIterator()
88
+ has_extra_options = NautobotBoolIterator()
89
+ extra_options = factory.Maybe(
90
+ "has_extra_options", factory.Faker("pydict", nb_elements=2, value_types=[str, bool, int]), None
91
+ )
92
+ has_tenant = NautobotBoolIterator()
93
+ tenant = factory.Maybe("has_tenant", random_instance(Tenant), None)
94
+
95
+ @factory.post_generation
96
+ def vpn_phase1_policies(self, create, extracted, **kwargs):
97
+ if create:
98
+ if extracted:
99
+ self.vpn_phase1_policies.set(extracted)
100
+ else:
101
+ self.vpn_phase1_policies.set(get_random_instances(models.VPNPhase1Policy, minimum=1))
102
+
103
+ @factory.post_generation
104
+ def vpn_phase2_policies(self, create, extracted, **kwargs):
105
+ if create:
106
+ if extracted:
107
+ self.vpn_phase2_policies.set(extracted)
108
+ else:
109
+ self.vpn_phase2_policies.set(get_random_instances(models.VPNPhase2Policy, minimum=1))
110
+
111
+
112
+ class VPNFactory(PrimaryModelFactory):
113
+ class Meta:
114
+ model = models.VPN
115
+ exclude = ("has_description", "has_profile", "has_role", "has_tenant")
116
+
117
+ name = UniqueFaker("word")
118
+ has_description = NautobotBoolIterator()
119
+ description = factory.Maybe("has_description", factory.Faker("sentence"), "")
120
+ vpn_id = factory.Faker("word")
121
+ has_profile = NautobotBoolIterator()
122
+ vpn_profile = factory.Maybe("has_profile", random_instance(models.VPNProfile), None)
123
+ has_role = NautobotBoolIterator()
124
+ role = factory.Maybe(
125
+ "has_role",
126
+ random_instance(lambda: Role.objects.get_for_model(models.VPN), allow_null=True),
127
+ None,
128
+ )
129
+ has_tenant = NautobotBoolIterator()
130
+ tenant = factory.Maybe("has_tenant", random_instance(Tenant), None)
131
+
132
+
133
+ class VPNTunnelFactory(PrimaryModelFactory):
134
+ class Meta:
135
+ model = models.VPNTunnel
136
+ exclude = ("has_description", "has_profile", "has_vpn", "has_role", "has_tenant")
137
+
138
+ name = UniqueFaker("word")
139
+ has_description = NautobotBoolIterator()
140
+ description = factory.Maybe("has_description", factory.Faker("sentence"), "")
141
+ tunnel_id = factory.Faker("word")
142
+ has_profile = NautobotBoolIterator()
143
+ vpn_profile = factory.Maybe("has_profile", random_instance(models.VPNProfile), None)
144
+ has_vpn = NautobotBoolIterator()
145
+ vpn = factory.Maybe("has_vpn", random_instance(models.VPN), None)
146
+ status = random_instance(
147
+ lambda: Status.objects.get_for_model(models.VPNTunnel),
148
+ allow_null=False,
149
+ )
150
+ has_role = NautobotBoolIterator()
151
+ role = factory.Maybe(
152
+ "has_role",
153
+ random_instance(lambda: Role.objects.get_for_model(models.VPNTunnel), allow_null=True),
154
+ None,
155
+ )
156
+ encapsulation = factory.Faker("random_element", elements=choices.EncapsulationChoices.values())
157
+ endpoint_a = factory.Sequence(lambda n: models.VPNTunnelEndpoint.objects.all()[n])
158
+ endpoint_z = factory.Sequence(lambda n: models.VPNTunnelEndpoint.objects.all()[n + 10])
159
+ has_tenant = NautobotBoolIterator()
160
+ tenant = factory.Maybe("has_tenant", random_instance(Tenant), None)
161
+
162
+
163
+ class VPNTunnelEndpointFactory(PrimaryModelFactory):
164
+ class Meta:
165
+ model = models.VPNTunnelEndpoint
166
+ exclude = ("has_device", "has_profile", "has_role", "has_tenant")
167
+
168
+ has_device = NautobotBoolIterator()
169
+ device = factory.Maybe("has_device", random_instance(Device), None)
170
+ source_interface = factory.Maybe(
171
+ "has_device",
172
+ random_instance(
173
+ lambda: Interface.objects.filter(vpn_tunnel_endpoints_src_int__isnull=True, device__isnull=False)
174
+ ),
175
+ None,
176
+ )
177
+ source_ipaddress = factory.Maybe("has_device", random_instance(IPAddress), None)
178
+ source_fqdn = factory.Maybe("has_device", "", factory.Faker("word"))
179
+ tunnel_interface = factory.Maybe(
180
+ "has_device",
181
+ random_instance(lambda: Interface.objects.filter(type="tunnel", vpn_tunnel_endpoints_tunnel__isnull=True)),
182
+ None,
183
+ )
184
+ has_profile = NautobotBoolIterator()
185
+ vpn_profile = factory.Maybe("has_profile", random_instance(models.VPNProfile), None)
186
+ has_role = NautobotBoolIterator()
187
+ role = factory.Maybe(
188
+ "has_role",
189
+ random_instance(lambda: Role.objects.get_for_model(models.VPNTunnelEndpoint), allow_null=True),
190
+ None,
191
+ )
192
+ has_tenant = NautobotBoolIterator()
193
+ tenant = factory.Maybe("has_tenant", random_instance(Tenant), None)
194
+
195
+ @factory.post_generation
196
+ def protected_prefixes(self, create, extracted, **kwargs):
197
+ if create:
198
+ if extracted:
199
+ self.protected_prefixes.set(extracted)
200
+ else:
201
+ self.protected_prefixes.set(get_random_instances(Prefix, minimum=0))
202
+
203
+ @factory.post_generation
204
+ def protected_prefixes_dg(self, create, extracted, **kwargs):
205
+ if create:
206
+ if extracted:
207
+ self.protected_prefixes_dg.set(extracted)
208
+ else:
209
+ self.protected_prefixes_dg.set(get_random_instances(DynamicGroup, minimum=0))