nautobot 2.4.21__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.
- nautobot/apps/choices.py +2 -2
- nautobot/apps/filters.py +9 -9
- nautobot/apps/forms.py +2 -0
- nautobot/apps/models.py +7 -2
- nautobot/apps/ui.py +20 -1
- nautobot/apps/utils.py +2 -3
- nautobot/apps/views.py +7 -1
- nautobot/circuits/filters.py +8 -23
- nautobot/circuits/navigation.py +3 -1
- nautobot/circuits/templates/circuits/circuit_create.html +9 -9
- nautobot/circuits/templates/circuits/circuit_terminations_swap.html +2 -2
- nautobot/circuits/templates/circuits/circuittermination_create.html +24 -33
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +10 -10
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +13 -13
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +6 -6
- nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +3 -3
- nautobot/circuits/templates/circuits/inc/speed_widget.html +13 -13
- nautobot/circuits/templates/circuits/provider_create.html +9 -9
- nautobot/circuits/tests/integration/test_circuit.py +19 -19
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +3 -0
- nautobot/circuits/tests/integration/test_relationships.py +4 -12
- nautobot/circuits/views.py +0 -2
- nautobot/cloud/filters.py +1 -13
- nautobot/cloud/navigation.py +3 -1
- nautobot/cloud/templates/cloud/cloudnetwork_update.html +9 -9
- nautobot/cloud/templates/cloud/cloudservice_update.html +6 -6
- nautobot/core/api/fields.py +30 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +9 -2
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +58 -37
- nautobot/core/apps/__init__.py +6 -12
- nautobot/core/branching.py +83 -0
- nautobot/core/celery/__init__.py +11 -6
- nautobot/core/celery/backends.py +2 -0
- nautobot/core/celery/encoders.py +7 -0
- nautobot/core/celery/task.py +44 -0
- nautobot/core/checks.py +60 -0
- nautobot/core/cli/bootstrap_v3_to_v5.py +776 -0
- nautobot/core/constants.py +9 -0
- nautobot/core/context_processors.py +84 -0
- nautobot/core/filters.py +131 -2
- nautobot/core/forms/__init__.py +4 -2
- nautobot/core/forms/fields.py +10 -8
- nautobot/core/forms/forms.py +21 -9
- nautobot/core/forms/search.py +0 -15
- nautobot/core/forms/widgets.py +3 -2
- nautobot/core/graphql/__init__.py +8 -26
- nautobot/core/graphql/generators.py +16 -6
- nautobot/core/graphql/schema.py +1 -1
- nautobot/core/graphql/schema_init.py +1 -2
- nautobot/core/graphql/utils.py +7 -9
- nautobot/core/jobs/__init__.py +158 -0
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +17 -2
- nautobot/core/models/fields.py +3 -2
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/name_color_content_types.py +1 -1
- nautobot/core/models/ordering.py +7 -5
- nautobot/core/models/querysets.py +77 -2
- nautobot/core/models/tree_queries.py +6 -4
- nautobot/core/settings.py +30 -16
- nautobot/core/settings.yaml +13 -7
- nautobot/core/tables.py +114 -44
- nautobot/core/templates/403.html +1 -1
- nautobot/core/templates/403_csrf_failure.html +1 -1
- nautobot/core/templates/404.html +1 -1
- nautobot/core/templates/40x.html +8 -8
- nautobot/core/templates/500.html +10 -10
- nautobot/core/templates/about.html +13 -12
- nautobot/core/templates/admin/actions.html +1 -1
- nautobot/core/templates/admin/app_index.html +3 -3
- nautobot/core/templates/admin/base.html +45 -52
- nautobot/core/templates/admin/base_site.html +0 -9
- nautobot/core/templates/admin/change_form.html +5 -5
- nautobot/core/templates/admin/change_list.html +8 -12
- nautobot/core/templates/admin/change_list_results.html +3 -3
- nautobot/core/templates/admin/config/config.html +24 -24
- nautobot/core/templates/admin/delete_confirmation.html +5 -5
- nautobot/core/templates/admin/edit_inline/stacked.html +5 -5
- nautobot/core/templates/admin/edit_inline/tabular.html +3 -3
- nautobot/core/templates/admin/includes/fieldset.html +15 -15
- nautobot/core/templates/admin/index.html +8 -8
- nautobot/core/templates/admin/submit_line.html +5 -5
- nautobot/core/templates/base_django.html +36 -32
- nautobot/core/templates/buttons/add.html +1 -1
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +2 -2
- nautobot/core/templates/buttons/export.html +17 -18
- nautobot/core/templates/buttons/job_import.html +2 -2
- nautobot/core/templates/components/breadcrumbs.html +19 -17
- nautobot/core/templates/components/button/dropdown.html +7 -5
- nautobot/core/templates/components/echarts.html +2 -0
- nautobot/core/templates/components/layout/one_over_two.html +3 -3
- nautobot/core/templates/components/layout/two_over_one.html +3 -3
- nautobot/core/templates/components/panel/body_content_data_table.html +2 -2
- nautobot/core/templates/components/panel/body_content_tags.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_generic.html +4 -2
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_table.html +4 -2
- nautobot/core/templates/components/panel/footer_contacts_table.html +4 -4
- nautobot/core/templates/components/panel/footer_content_table.html +3 -3
- nautobot/core/templates/components/panel/grouping_toggle.html +12 -11
- nautobot/core/templates/components/panel/header_extra_content_table.html +2 -11
- nautobot/core/templates/components/panel/panel.html +6 -3
- nautobot/core/templates/components/panel/stats_panel_body.html +9 -7
- nautobot/core/templates/components/tab/content_wrapper.html +29 -1
- nautobot/core/templates/components/tab/label_wrapper.html +10 -2
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +11 -4
- nautobot/core/templates/echarts/echarts.html +20 -0
- nautobot/core/templates/exceptions/import_error.html +2 -2
- nautobot/core/templates/exceptions/permission_error.html +1 -1
- nautobot/core/templates/exceptions/programming_error.html +2 -2
- nautobot/core/templates/generic/object_bulk_add_component.html +29 -20
- nautobot/core/templates/generic/object_bulk_create.html +87 -75
- nautobot/core/templates/generic/object_bulk_destroy.html +35 -37
- nautobot/core/templates/generic/object_bulk_remove.html +30 -26
- nautobot/core/templates/generic/object_bulk_rename.html +53 -40
- nautobot/core/templates/generic/object_bulk_update.html +36 -29
- nautobot/core/templates/generic/object_create.html +40 -27
- nautobot/core/templates/generic/object_import.html +36 -24
- nautobot/core/templates/generic/object_list.html +279 -215
- nautobot/core/templates/generic/object_notes.html +21 -11
- nautobot/core/templates/generic/object_retrieve.html +161 -213
- nautobot/core/templates/graphene/graphiql.html +113 -60
- nautobot/core/templates/home.html +164 -87
- nautobot/core/templates/import_success.html +3 -2
- nautobot/core/templates/inc/ajax_loader.html +1 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +25 -13
- nautobot/core/templates/inc/created_updated.html +12 -7
- nautobot/core/templates/inc/custom_fields/panel_data.html +28 -16
- nautobot/core/templates/inc/custom_fields_panel.html +3 -3
- nautobot/core/templates/inc/dynamic_groups_panel.html +3 -3
- nautobot/core/templates/inc/extras_features_edit_form_fields.html +15 -15
- nautobot/core/templates/inc/footer.html +90 -40
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +75 -0
- nautobot/core/templates/inc/header_banners.html +17 -0
- nautobot/core/templates/inc/header_messages.html +6 -0
- nautobot/core/templates/inc/image_attachments.html +9 -9
- nautobot/core/templates/inc/javascript.html +7 -24
- nautobot/core/templates/inc/media.html +4 -29
- nautobot/core/templates/inc/modal.html +2 -2
- nautobot/core/templates/inc/nav_favorites.html +27 -0
- nautobot/core/templates/inc/nav_menu.html +150 -108
- nautobot/core/templates/inc/object_details_advanced_panel.html +84 -71
- nautobot/core/templates/inc/page_title.html +23 -0
- nautobot/core/templates/inc/paginator.html +39 -28
- nautobot/core/templates/inc/relationships/panel_override.html +3 -3
- nautobot/core/templates/inc/relationships_panel.html +3 -3
- nautobot/core/templates/inc/relationships_table_rows.html +1 -1
- nautobot/core/templates/inc/search_panel.html +22 -16
- nautobot/core/templates/inc/table.html +61 -36
- nautobot/core/templates/inc/tenancy_form_panel.html +3 -3
- nautobot/core/templates/login.html +17 -59
- nautobot/core/templates/modals/modal_theme.html +12 -23
- nautobot/core/templates/nautobot_config.py.j2 +6 -5
- nautobot/core/templates/panel_table.html +8 -12
- nautobot/core/templates/redoc_ui.html +80 -0
- nautobot/core/templates/rest_framework/api.html +43 -21
- nautobot/core/templates/search.html +12 -13
- nautobot/core/templates/swagger_ui.html +19 -4
- nautobot/core/templates/system_jobs/import_objects.html +70 -58
- nautobot/core/templates/template.css +0 -6
- nautobot/core/templates/utilities/comment_form.html +34 -0
- nautobot/core/templates/utilities/confirmation_form.html +17 -9
- nautobot/core/templates/utilities/obj_table.html +19 -11
- nautobot/core/templates/utilities/render_field.html +27 -21
- nautobot/core/templates/utilities/render_jinja2.html +22 -25
- nautobot/core/templates/utilities/templatetags/advanced_filter_indicator.html +8 -0
- nautobot/core/templates/utilities/templatetags/badge.html +1 -1
- nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +2 -3
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +482 -0
- nautobot/core/templates/utilities/templatetags/modal_form_as_dialog.html +14 -18
- nautobot/core/templates/utilities/templatetags/saved_view_modal.html +11 -11
- nautobot/core/templates/utilities/templatetags/table_config_form.html +51 -24
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/templatetags/utilization_graph.html +3 -3
- nautobot/core/templates/utilities/theme_preview.html +829 -566
- nautobot/core/templates/utilities/worker_status.html +42 -41
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templates/widgets/sluginput.html +2 -2
- nautobot/core/templatetags/buttons.py +38 -40
- nautobot/core/templatetags/helpers.py +105 -28
- nautobot/core/templatetags/ui_framework.py +17 -0
- nautobot/core/testing/api.py +76 -12
- nautobot/core/testing/filters.py +11 -27
- nautobot/core/testing/integration.py +128 -10
- nautobot/core/testing/mixins.py +7 -4
- nautobot/core/testing/utils.py +28 -5
- nautobot/core/testing/views.py +125 -27
- nautobot/core/tests/integration/test_app_home.py +39 -35
- nautobot/core/tests/integration/test_app_navbar.py +60 -67
- nautobot/core/tests/integration/test_filters.py +123 -55
- nautobot/core/tests/integration/test_general_functionality.py +1 -1
- nautobot/core/tests/integration/test_home.py +10 -18
- nautobot/core/tests/integration/test_import_objects_ui.py +2 -9
- nautobot/core/tests/integration/test_navbar.py +41 -16
- nautobot/core/tests/integration/test_swagger.py +1 -7
- nautobot/core/tests/integration/test_theme.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +6 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_branching.py +154 -0
- nautobot/core/tests/test_breadcrumbs.py +7 -8
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_commands.py +0 -41
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_csv.py +4 -7
- nautobot/core/tests/test_filters.py +326 -318
- nautobot/core/tests/test_forms.py +19 -30
- nautobot/core/tests/test_graphql.py +67 -57
- nautobot/core/tests/test_models.py +1 -1
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +78 -10
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +61 -21
- nautobot/core/tests/test_templatetags_ui_framework.py +36 -18
- nautobot/core/tests/test_ui.py +207 -2
- nautobot/core/tests/test_utils.py +147 -2
- nautobot/core/tests/test_views.py +201 -64
- nautobot/core/tests/test_views_utils.py +1 -1
- nautobot/core/ui/breadcrumbs.py +2 -12
- nautobot/core/ui/choices.py +190 -0
- nautobot/core/ui/constants.py +86 -0
- nautobot/core/ui/echarts.py +474 -0
- nautobot/core/ui/nav.py +5 -1
- nautobot/core/ui/object_detail.py +180 -16
- nautobot/core/urls.py +13 -1
- nautobot/core/utils/cache.py +71 -0
- nautobot/core/utils/data.py +8 -5
- nautobot/core/utils/filtering.py +8 -2
- nautobot/core/utils/git.py +3 -3
- nautobot/core/utils/lookup.py +87 -13
- nautobot/core/utils/migrations.py +22 -0
- nautobot/core/utils/module_loading.py +26 -0
- nautobot/core/utils/permissions.py +9 -5
- nautobot/core/views/__init__.py +114 -63
- nautobot/core/views/generic.py +34 -27
- nautobot/core/views/mixins.py +49 -27
- nautobot/core/views/renderers.py +3 -5
- nautobot/core/views/utils.py +10 -5
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/__init__.py +0 -0
- nautobot/data_validation/api/__init__.py +1 -0
- nautobot/data_validation/api/serializers.py +80 -0
- nautobot/data_validation/api/urls.py +20 -0
- nautobot/data_validation/api/views.py +44 -0
- nautobot/data_validation/apps.py +18 -0
- nautobot/data_validation/custom_validators.py +330 -0
- nautobot/data_validation/filters.py +133 -0
- nautobot/data_validation/form_mixin.py +25 -0
- nautobot/data_validation/forms.py +342 -0
- nautobot/data_validation/migrations/0001_initial.py +224 -0
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +324 -0
- nautobot/data_validation/migrations/__init__.py +0 -0
- nautobot/data_validation/models.py +361 -0
- nautobot/data_validation/navigation.py +74 -0
- nautobot/data_validation/signals.py +30 -0
- nautobot/data_validation/tables.py +259 -0
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +1 -0
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +11 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +20 -0
- nautobot/data_validation/tests/migrations/__init__.py +0 -0
- nautobot/data_validation/tests/migrations/test_migrations.py +489 -0
- nautobot/data_validation/tests/test_api.py +238 -0
- nautobot/data_validation/tests/test_custom_validators.py +423 -0
- nautobot/data_validation/tests/test_data_compliance_rules.py +85 -0
- nautobot/data_validation/tests/test_filters.py +240 -0
- nautobot/data_validation/tests/test_form_mixin.py +115 -0
- nautobot/data_validation/tests/test_models.py +393 -0
- nautobot/data_validation/tests/test_views.py +435 -0
- nautobot/data_validation/urls.py +21 -0
- nautobot/data_validation/views.py +227 -0
- nautobot/dcim/api/serializers.py +10 -13
- nautobot/dcim/api/urls.py +2 -0
- nautobot/dcim/api/views.py +7 -0
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +16 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +70 -157
- nautobot/dcim/forms.py +12 -6
- nautobot/dcim/graphql/types.py +1 -0
- nautobot/dcim/migrations/0075_add_deviceclusterassignment.py +52 -0
- nautobot/dcim/migrations/0076_device_cluster_to_clusters_data_migration.py +40 -0
- nautobot/dcim/migrations/0077_remove_device_cluster.py +14 -0
- nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
- nautobot/dcim/models/__init__.py +2 -0
- nautobot/dcim/models/device_components.py +3 -1
- nautobot/dcim/models/devices.py +115 -51
- nautobot/dcim/navigation.py +7 -3
- nautobot/dcim/querysets.py +6 -0
- nautobot/dcim/signals.py +19 -0
- nautobot/dcim/tables/devices.py +9 -5
- nautobot/dcim/tables/template_code.py +191 -102
- nautobot/dcim/templates/dcim/cable.html +1 -1
- nautobot/dcim/templates/dcim/cable_connect.html +62 -146
- nautobot/dcim/templates/dcim/cable_retrieve.html +10 -10
- nautobot/dcim/templates/dcim/cable_trace.html +15 -17
- nautobot/dcim/templates/dcim/console_port_connection_list.html +2 -2
- nautobot/dcim/templates/dcim/consoleport.html +18 -17
- nautobot/dcim/templates/dcim/consoleserverport.html +18 -17
- nautobot/dcim/templates/dcim/controller_create.html +12 -8
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +1 -1
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +6 -6
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/device/config.html +17 -19
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +4 -4
- nautobot/dcim/templates/dcim/device/status.html +20 -20
- nautobot/dcim/templates/dcim/device_component_add.html +24 -15
- nautobot/dcim/templates/dcim/device_create.html +120 -120
- nautobot/dcim/templates/dcim/device_list.html +75 -12
- nautobot/dcim/templates/dcim/devicebay.html +7 -7
- nautobot/dcim/templates/dcim/devicebay_populate.html +29 -23
- nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +6 -6
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/devicetype_component_add.html +25 -19
- nautobot/dcim/templates/dcim/devicetype_list.html +4 -4
- nautobot/dcim/templates/dcim/devicetype_update.html +9 -9
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +3 -3
- nautobot/dcim/templates/dcim/frontport.html +21 -20
- nautobot/dcim/templates/dcim/inc/cable_form.html +7 -7
- nautobot/dcim/templates/dcim/inc/cable_termination.html +1 -1
- nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +18 -9
- nautobot/dcim/templates/dcim/inc/detail_softwareversion_softwareimagefile_rows.html +1 -1
- nautobot/dcim/templates/dcim/inc/device_interface_filter.html +1 -1
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +10 -10
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/homepage_connections.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +10 -10
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +2 -2
- nautobot/dcim/templates/dcim/interface.html +42 -22
- nautobot/dcim/templates/dcim/interface_connection_list.html +2 -2
- nautobot/dcim/templates/dcim/interface_edit.html +26 -11
- nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +3 -3
- nautobot/dcim/templates/dcim/inventoryitem.html +3 -3
- nautobot/dcim/templates/dcim/inventoryitem_add.html +21 -10
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +6 -4
- nautobot/dcim/templates/dcim/location.html +1 -1
- nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +24 -18
- nautobot/dcim/templates/dcim/location_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/location_update.html +9 -9
- nautobot/dcim/templates/dcim/locationtype.html +0 -1
- nautobot/dcim/templates/dcim/module/base.html +67 -27
- nautobot/dcim/templates/dcim/module_consoleports.html +13 -15
- nautobot/dcim/templates/dcim/module_consoleserverports.html +13 -15
- nautobot/dcim/templates/dcim/module_frontports.html +13 -15
- nautobot/dcim/templates/dcim/module_interfaces.html +14 -16
- nautobot/dcim/templates/dcim/module_list.html +59 -10
- nautobot/dcim/templates/dcim/module_modulebays.html +12 -14
- nautobot/dcim/templates/dcim/module_poweroutlets.html +13 -15
- nautobot/dcim/templates/dcim/module_powerports.html +13 -15
- nautobot/dcim/templates/dcim/module_rearports.html +13 -15
- nautobot/dcim/templates/dcim/module_retrieve.html +3 -3
- nautobot/dcim/templates/dcim/module_update.html +15 -9
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -93
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +7 -7
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +74 -35
- nautobot/dcim/templates/dcim/platform_create.html +9 -9
- nautobot/dcim/templates/dcim/power_port_connection_list.html +3 -3
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed_edit.html +15 -15
- nautobot/dcim/templates/dcim/poweroutlet.html +13 -13
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +17 -16
- nautobot/dcim/templates/dcim/rack.html +1 -1
- nautobot/dcim/templates/dcim/rack_elevation.html +2 -2
- nautobot/dcim/templates/dcim/rack_elevation_list.html +21 -9
- nautobot/dcim/templates/dcim/rack_retrieve.html +75 -57
- nautobot/dcim/templates/dcim/rack_update.html +14 -14
- nautobot/dcim/templates/dcim/rackreservation.html +1 -1
- nautobot/dcim/templates/dcim/rackreservation_edit.html +6 -6
- nautobot/dcim/templates/dcim/rearport.html +19 -18
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/trace/circuit.html +1 -1
- nautobot/dcim/templates/dcim/trace/device.html +1 -1
- nautobot/dcim/templates/dcim/trace/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/trace/termination.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +25 -16
- nautobot/dcim/templates/dcim/virtualchassis_create.html +6 -6
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_update.html +36 -22
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +9 -9
- nautobot/dcim/tests/integration/test_controller.py +6 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +7 -7
- nautobot/dcim/tests/integration/test_create_device.py +9 -9
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +7 -2
- nautobot/dcim/tests/integration/test_fileinputpicker.py +5 -7
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +2 -0
- nautobot/dcim/tests/integration/test_module_bay_position.py +4 -1
- nautobot/dcim/tests/test_api.py +86 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +159 -110
- nautobot/dcim/tests/test_graphql.py +32 -36
- nautobot/dcim/tests/test_jobs.py +1 -1
- nautobot/dcim/tests/test_models.py +229 -1
- nautobot/dcim/tests/test_views.py +31 -20
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +77 -41
- nautobot/extras/api/serializers.py +83 -19
- nautobot/extras/api/urls.py +7 -0
- nautobot/extras/api/views.py +243 -140
- nautobot/extras/choices.py +34 -13
- nautobot/extras/constants.py +1 -1
- nautobot/extras/context_managers.py +26 -26
- nautobot/extras/datasources/git.py +22 -0
- nautobot/extras/datasources/registry.py +3 -0
- nautobot/extras/exceptions.py +5 -0
- nautobot/extras/factory.py +11 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +4 -3
- nautobot/extras/{filters/__init__.py → filters.py} +203 -58
- nautobot/extras/forms/base.py +2 -1
- nautobot/extras/forms/forms.py +225 -20
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/homepage.py +21 -2
- nautobot/extras/jobs.py +2 -8
- nautobot/extras/jobs_ui.py +2 -2
- nautobot/extras/management/__init__.py +9 -0
- nautobot/extras/managers.py +31 -22
- nautobot/extras/migrations/0126_approval_workflow_pre_check.py +58 -0
- nautobot/extras/migrations/0127_approval_workflow_models.py +266 -0
- nautobot/extras/migrations/0128_remove_job_approval_required_and_more.py +29 -0
- nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
- nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
- nautobot/extras/models/__init__.py +14 -3
- nautobot/extras/models/approvals.py +556 -0
- nautobot/extras/models/change_logging.py +1 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/customfields.py +57 -22
- nautobot/extras/models/datasources.py +21 -0
- nautobot/extras/models/groups.py +2 -0
- nautobot/extras/models/jobs.py +122 -39
- nautobot/extras/models/metadata.py +2 -3
- nautobot/extras/models/mixins.py +129 -1
- nautobot/extras/models/models.py +22 -14
- nautobot/extras/models/relationships.py +47 -10
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/models/tags.py +1 -1
- nautobot/extras/navigation.py +42 -15
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +1 -23
- nautobot/extras/plugins/tables.py +8 -6
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/validators.py +10 -10
- nautobot/extras/plugins/views.py +1 -5
- nautobot/extras/querysets.py +17 -21
- nautobot/extras/signals.py +23 -8
- nautobot/extras/tables.py +420 -99
- nautobot/extras/templates/extras/approval_dashboard.html +15 -0
- nautobot/extras/templates/extras/approval_workflow/approve.html +11 -0
- nautobot/extras/templates/extras/approval_workflow/comment.html +9 -0
- nautobot/extras/templates/extras/approval_workflow/deny.html +10 -0
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +77 -0
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +29 -0
- nautobot/extras/templates/extras/configcontext_update.html +12 -12
- nautobot/extras/templates/extras/configcontextschema.html +1 -1
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +9 -9
- nautobot/extras/templates/extras/configcontextschema_update.html +6 -6
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/customfield_update.html +12 -12
- nautobot/extras/templates/extras/dynamicgroup.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_edit.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +17 -17
- nautobot/extras/templates/extras/dynamicgroup_update.html +24 -24
- nautobot/extras/templates/extras/externalintegration_update.html +6 -6
- nautobot/extras/templates/extras/gitrepository.html +1 -1
- nautobot/extras/templates/extras/gitrepository_object_edit.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +1 -1
- nautobot/extras/templates/extras/gitrepository_retrieve.html +12 -12
- nautobot/extras/templates/extras/gitrepository_update.html +25 -7
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -1
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +38 -0
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +14 -13
- nautobot/extras/templates/extras/inc/configcontext_format.html +11 -4
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +7 -7
- nautobot/extras/templates/extras/inc/job_label.html +5 -5
- nautobot/extras/templates/extras/inc/job_table.html +23 -10
- nautobot/extras/templates/extras/inc/job_tiles.html +33 -21
- nautobot/extras/templates/extras/inc/jobresult.html +6 -6
- nautobot/extras/templates/extras/inc/json_format.html +11 -4
- nautobot/extras/templates/extras/inc/object_contact_header.html +6 -6
- nautobot/extras/templates/extras/inc/overridable_field.html +16 -15
- nautobot/extras/templates/extras/inc/panel_approvalworkflowstage.html +34 -0
- nautobot/extras/templates/extras/inc/panel_changelog.html +9 -9
- nautobot/extras/templates/extras/inc/panel_jobhistory.html +8 -6
- nautobot/extras/templates/extras/inc/tags_panel.html +3 -3
- nautobot/extras/templates/extras/job.html +154 -155
- nautobot/extras/templates/extras/job_approval_confirmation.html +4 -27
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -1
- nautobot/extras/templates/extras/job_detail.html +1 -1
- nautobot/extras/templates/extras/job_edit.html +69 -64
- nautobot/extras/templates/extras/job_list.html +37 -60
- nautobot/extras/templates/extras/jobresult.html +1 -1
- nautobot/extras/templates/extras/jobresult_retrieve.html +17 -17
- nautobot/extras/templates/extras/marketplace.html +62 -71
- nautobot/extras/templates/extras/metadatatype_create.html +9 -9
- nautobot/extras/templates/extras/note.html +1 -1
- nautobot/extras/templates/extras/object_approvalworkflow.html +36 -0
- nautobot/extras/templates/extras/object_assign_contact_or_team.html +16 -7
- nautobot/extras/templates/extras/object_configcontext.html +20 -20
- nautobot/extras/templates/extras/object_new_contact.html +6 -6
- nautobot/extras/templates/extras/object_new_team.html +6 -6
- nautobot/extras/templates/extras/objectchange.html +1 -1
- nautobot/extras/templates/extras/objectchange_retrieve.html +37 -56
- nautobot/extras/templates/extras/plugin_detail.html +40 -41
- nautobot/extras/templates/extras/plugins_list.html +23 -38
- nautobot/extras/templates/extras/plugins_tiles.html +28 -28
- nautobot/extras/templates/extras/role_retrieve.html +112 -48
- nautobot/extras/templates/extras/scheduledjob.html +25 -28
- nautobot/extras/templates/extras/secret_create.html +11 -11
- nautobot/extras/templates/extras/secretsgroup_update.html +6 -6
- nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +3 -3
- nautobot/extras/templates/extras/status.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templates/extras/tag_update.html +3 -3
- nautobot/extras/templates/extras/templatetags/log_level.html +1 -1
- nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -2
- nautobot/extras/templates/extras/webhook.html +12 -12
- nautobot/extras/templatetags/approvals.py +19 -0
- nautobot/extras/templatetags/custom_links.py +12 -12
- nautobot/extras/templatetags/job_buttons.py +14 -12
- nautobot/extras/test_jobs/invalid_import.py +9 -0
- nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
- nautobot/extras/test_jobs/missing_import.py +11 -0
- nautobot/extras/tests/integration/test_computedfields.py +5 -8
- nautobot/extras/tests/integration/test_configcontextschema.py +43 -48
- nautobot/extras/tests/integration/test_customfields.py +33 -33
- nautobot/extras/tests/integration/test_dynamicgroups.py +5 -10
- nautobot/extras/tests/integration/test_jobs.py +2 -4
- nautobot/extras/tests/integration/test_notes.py +3 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +35 -27
- nautobot/extras/tests/integration/test_relationships.py +7 -11
- nautobot/extras/tests/integration/test_tagfilter.py +3 -11
- nautobot/extras/tests/test_api.py +786 -242
- nautobot/extras/tests/test_approvals.py +715 -0
- nautobot/extras/tests/test_changelog.py +18 -14
- nautobot/extras/tests/test_customfields.py +14 -13
- nautobot/extras/tests/test_datasources.py +1 -1
- nautobot/extras/tests/test_dynamicgroups.py +9 -4
- nautobot/extras/tests/test_filters.py +443 -13
- nautobot/extras/tests/test_forms.py +18 -57
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +81 -1
- nautobot/extras/tests/test_models.py +378 -47
- nautobot/extras/tests/test_plugins.py +47 -13
- nautobot/extras/tests/test_relationships.py +7 -2
- nautobot/extras/tests/test_utils.py +2 -0
- nautobot/extras/tests/test_views.py +780 -493
- nautobot/extras/urls.py +36 -12
- nautobot/extras/utils.py +58 -12
- nautobot/extras/views.py +668 -209
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +35 -71
- nautobot/ipam/formfields.py +1 -1
- nautobot/ipam/forms.py +6 -3
- nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
- nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
- nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
- nautobot/ipam/models.py +29 -2
- nautobot/ipam/navigation.py +3 -1
- nautobot/ipam/querysets.py +1 -2
- nautobot/ipam/tables.py +26 -17
- nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +6 -6
- nautobot/ipam/templates/ipam/inc/service.html +8 -8
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +3 -2
- nautobot/ipam/templates/ipam/ipaddress.html +27 -13
- nautobot/ipam/templates/ipam/ipaddress_assign.html +31 -24
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +3 -3
- nautobot/ipam/templates/ipam/ipaddress_edit.html +9 -9
- nautobot/ipam/templates/ipam/ipaddress_interfaces.html +7 -9
- nautobot/ipam/templates/ipam/ipaddress_merge.html +195 -186
- nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +7 -9
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +7 -5
- nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
- nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
- nautobot/ipam/templates/ipam/namespace_update.html +15 -0
- nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
- nautobot/ipam/templates/ipam/prefix_create.html +9 -9
- nautobot/ipam/templates/ipam/prefix_list.html +15 -14
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_update.html +6 -6
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/templates/ipam/vrf_edit.html +15 -15
- nautobot/ipam/tests/integration/test_prefixes.py +5 -13
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +20 -7
- nautobot/ipam/tests/test_filters.py +10 -0
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +1 -1
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +24 -21
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +49 -7
- nautobot/project-static/dist/css/graphql-libraries.css +655 -0
- nautobot/project-static/dist/css/graphql-libraries.css.map +1 -0
- nautobot/project-static/dist/css/materialdesignicons.css +3 -0
- nautobot/project-static/dist/css/materialdesignicons.css.map +1 -0
- nautobot/project-static/dist/css/nautobot.css +13 -0
- nautobot/project-static/dist/css/nautobot.css.map +1 -0
- nautobot/project-static/dist/js/graphql-libraries.js +3 -0
- nautobot/project-static/dist/js/graphql-libraries.js.LICENSE.txt +62 -0
- nautobot/project-static/dist/js/graphql-libraries.js.map +1 -0
- nautobot/project-static/dist/js/libraries.js +3 -0
- nautobot/project-static/dist/js/libraries.js.LICENSE.txt +65 -0
- nautobot/project-static/dist/js/libraries.js.map +1 -0
- nautobot/project-static/dist/js/materialdesignicons.js +0 -0
- nautobot/project-static/dist/js/nautobot-graphiql.js +2 -0
- nautobot/project-static/dist/js/nautobot-graphiql.js.map +1 -0
- nautobot/project-static/dist/js/nautobot.js +2 -0
- nautobot/project-static/dist/js/nautobot.js.map +1 -0
- nautobot/project-static/fonts/Montserrat-v30-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Montserrat-v30-Light.woff2 +0 -0
- nautobot/project-static/fonts/Montserrat-v30-Regular.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Light.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Regular.woff2 +0 -0
- nautobot/project-static/img/jinja_logo.svg +21 -92
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/editor.js +4 -4
- nautobot/project-static/js/forms.js +67 -717
- nautobot/project-static/js/job_result.js +2 -2
- nautobot/project-static/nautobot-icons/360-degrees.svg +3 -0
- nautobot/project-static/nautobot-icons/arrow-decision.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-expand-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-move-2-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-move-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/atom.svg +3 -0
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/branch.svg +3 -0
- nautobot/project-static/nautobot-icons/briefcase-2.svg +3 -0
- nautobot/project-static/nautobot-icons/cable-data-2.svg +3 -0
- nautobot/project-static/nautobot-icons/cable-data.svg +3 -0
- nautobot/project-static/nautobot-icons/cast.svg +3 -0
- nautobot/project-static/nautobot-icons/check-circle.svg +3 -0
- nautobot/project-static/nautobot-icons/checkbox-circle.svg +3 -0
- nautobot/project-static/nautobot-icons/checkbox-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-check.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-lightning.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-upload.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +3 -0
- nautobot/project-static/nautobot-icons/compass.svg +3 -0
- nautobot/project-static/nautobot-icons/control-panel.svg +3 -0
- nautobot/project-static/nautobot-icons/credit-card.svg +3 -0
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +3 -0
- nautobot/project-static/nautobot-icons/direction.svg +3 -0
- nautobot/project-static/nautobot-icons/elements.svg +3 -0
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/globe-2.svg +3 -0
- nautobot/project-static/nautobot-icons/globe.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +3 -0
- nautobot/project-static/nautobot-icons/history.svg +3 -0
- nautobot/project-static/nautobot-icons/ip.svg +3 -0
- nautobot/project-static/nautobot-icons/laptop.svg +3 -0
- nautobot/project-static/nautobot-icons/lightning.svg +3 -0
- nautobot/project-static/nautobot-icons/list-unordered.svg +3 -0
- nautobot/project-static/nautobot-icons/map-view.svg +3 -0
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/pin-2.svg +3 -0
- nautobot/project-static/nautobot-icons/pin-3.svg +3 -0
- nautobot/project-static/nautobot-icons/plug.svg +3 -0
- nautobot/project-static/nautobot-icons/refresh-cw.svg +3 -0
- nautobot/project-static/nautobot-icons/rocket-2.svg +3 -0
- nautobot/project-static/nautobot-icons/rotate-cw.svg +3 -0
- nautobot/project-static/nautobot-icons/route.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +3 -0
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server-2.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +3 -0
- nautobot/project-static/nautobot-icons/share.svg +3 -0
- nautobot/project-static/nautobot-icons/shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/sitemap-outline.svg +3 -0
- nautobot/project-static/nautobot-icons/sliders-vert-2.svg +3 -0
- nautobot/project-static/nautobot-icons/sliders-vert.svg +3 -0
- nautobot/project-static/nautobot-icons/star-filled.svg +3 -0
- nautobot/project-static/nautobot-icons/star.svg +3 -0
- nautobot/project-static/nautobot-icons/transform.svg +3 -0
- nautobot/project-static/nautobot-icons/wifi.svg +3 -0
- nautobot/tenancy/api/serializers.py +1 -0
- nautobot/tenancy/api/views.py +2 -1
- nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
- nautobot/tenancy/navigation.py +3 -1
- nautobot/tenancy/templates/tenancy/tenant_create.html +6 -6
- nautobot/tenancy/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/.gitignore +137 -0
- nautobot/ui/.node-version +1 -0
- nautobot/ui/.prettierignore +3 -0
- nautobot/ui/eslint.config.js +33 -0
- nautobot/ui/package-lock.json +6594 -0
- nautobot/ui/package.json +67 -0
- nautobot/ui/prettier.config.js +9 -0
- nautobot/ui/src/js/collapse.js +69 -0
- nautobot/ui/src/js/cookie.js +31 -0
- nautobot/ui/src/js/draggable.js +101 -0
- nautobot/ui/src/js/drawer.js +106 -0
- nautobot/ui/src/js/form.js +23 -0
- nautobot/ui/src/js/history.js +51 -0
- nautobot/ui/src/js/nautobot-graphiql.js +19 -0
- nautobot/ui/src/js/nautobot.js +128 -0
- nautobot/ui/src/js/search.js +274 -0
- nautobot/ui/src/js/select2.js +318 -0
- nautobot/ui/src/js/sidenav.js +87 -0
- nautobot/ui/src/js/tabs.js +139 -0
- nautobot/ui/src/js/theme.js +104 -0
- nautobot/ui/src/js/utils.js +54 -0
- nautobot/ui/src/scss/colors.scss +58 -0
- nautobot/ui/src/scss/nautobot.scss +2471 -0
- nautobot/ui/webpack.config.js +148 -0
- nautobot/users/apps.py +3 -0
- nautobot/users/filters.py +7 -11
- nautobot/users/forms.py +10 -0
- nautobot/users/models.py +8 -0
- nautobot/users/templates/users/advanced_settings_edit.html +31 -21
- nautobot/users/templates/users/api_tokens.html +61 -51
- nautobot/users/templates/users/base.html +23 -31
- nautobot/users/templates/users/change_password.html +29 -19
- nautobot/users/templates/users/preferences.html +55 -45
- nautobot/users/templates/users/profile.html +45 -14
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/urls.py +2 -0
- nautobot/users/views.py +70 -2
- nautobot/virtualization/api/views.py +1 -1
- nautobot/virtualization/filters.py +18 -32
- nautobot/virtualization/forms.py +22 -59
- nautobot/virtualization/models.py +1 -19
- nautobot/virtualization/navigation.py +3 -1
- nautobot/virtualization/tables.py +10 -6
- nautobot/virtualization/templates/virtualization/cluster.html +13 -13
- nautobot/virtualization/templates/virtualization/cluster_edit.html +6 -6
- nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +24 -16
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +4 -4
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +27 -25
- nautobot/virtualization/templates/virtualization/vminterface.html +5 -5
- nautobot/virtualization/templates/virtualization/vminterface_edit.html +27 -11
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_models.py +20 -5
- nautobot/virtualization/tests/test_views.py +3 -5
- nautobot/virtualization/urls.py +0 -11
- nautobot/virtualization/views.py +5 -122
- nautobot/vpn/__init__.py +0 -0
- nautobot/vpn/api/serializers.py +113 -0
- nautobot/vpn/api/urls.py +19 -0
- nautobot/vpn/api/views.py +70 -0
- nautobot/vpn/apps.py +8 -0
- nautobot/vpn/choices.py +171 -0
- nautobot/vpn/factory.py +209 -0
- nautobot/vpn/filters.py +233 -0
- nautobot/vpn/forms.py +486 -0
- nautobot/vpn/homepage.py +19 -0
- nautobot/vpn/migrations/0001_initial.py +541 -0
- nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
- nautobot/vpn/migrations/__init__.py +0 -0
- nautobot/vpn/models.py +527 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +380 -0
- nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +341 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +294 -0
- nautobot/vpn/tests/test_models.py +97 -0
- nautobot/vpn/tests/test_views.py +281 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +437 -0
- nautobot/wireless/filters.py +0 -8
- nautobot/wireless/navigation.py +3 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +6 -6
- nautobot/wireless/tests/integration/test_radio_profile.py +3 -7
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +5 -4
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +802 -707
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
- nautobot/core/management/commands/check_job_approval_status.py +0 -47
- nautobot/core/templates/search_form.html +0 -9
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +0 -87
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/extras/templates/extras/job_approval_request.html +0 -134
- nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +0 -28
- nautobot/ipam/mixins.py +0 -32
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +0 -587
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +0 -6865
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.eot +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.svg +0 -288
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.ttf +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.js +0 -2580
- nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.min.js +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/js/npm.js +0 -13
- nautobot/project-static/clipboard.js-2.0.9/clipboard.min.js +0 -7
- nautobot/project-static/css/base.css +0 -1040
- nautobot/project-static/css/dark.css +0 -282
- nautobot/project-static/flatpickr-4.6.9/flatpickr.min.js +0 -2
- nautobot/project-static/flatpickr-4.6.9/themes/light.min.css +0 -1
- nautobot/project-static/graphiql-1.5.16/graphiql.min.css +0 -12
- nautobot/project-static/graphiql-1.5.16/graphiql.min.js +0 -11
- nautobot/project-static/highlight.js-11.9.0/github-dark.min.css +0 -10
- nautobot/project-static/highlight.js-11.9.0/github.min.css +0 -10
- nautobot/project-static/highlight.js-11.9.0/highlight.min.js +0 -378
- nautobot/project-static/jquery/jquery-3.7.1.min.js +0 -2
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_444444_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_555555_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777620_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777777_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_cc0000_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_ffffff_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +0 -7
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +0 -6
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +0 -5
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.theme.min.css +0 -5
- nautobot/project-static/js/homepage_layout.js +0 -182
- nautobot/project-static/js/nav_menu.js +0 -250
- nautobot/project-static/js/theme.js +0 -133
- nautobot/project-static/materialdesignicons-7.4.47/LICENSE +0 -20
- nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +0 -3
- nautobot/project-static/react-16.14.0/react.production.min.js +0 -32
- nautobot/project-static/react-dom-16.14.0/react-dom.production.min.js +0 -239
- nautobot/project-static/select2-4.0.13/i18n/af.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ar.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/az.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bg.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bn.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bs.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ca.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/cs.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/da.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/de.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/dsb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/el.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/en.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/es.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/et.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/eu.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fa.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/gl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/he.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hsb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hu.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hy.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/id.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/is.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/it.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ja.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ka.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/km.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ko.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/lt.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/lv.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/mk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ms.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/nb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ne.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/nl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ps.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pt-BR.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pt.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ro.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ru.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sq.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sr-Cyrl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sv.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/th.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/tk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/tr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/uk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/vi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/zh-CN.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/zh-TW.js +0 -3
- nautobot/project-static/select2-4.0.13/select2.min.css +0 -1
- nautobot/project-static/select2-4.0.13/select2.min.js +0 -2
- nautobot/project-static/select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css +0 -7
- nautobot/project-static/subscriptions-transport-ws-0.9.18/client.min.js +0 -8
- nautobot/project-static/whatwg-fetch-3.6.2/fetch.umd.min.js +0 -8
- nautobot/virtualization/templates/virtualization/cluster_add_devices.html +0 -37
- /nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.ttf → dist/1fcc36272ea3e53d0031.ttf} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.eot → dist/2146c3c82b553977abc7.eot} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff → dist/e55a20c80650829ec5fd.woff} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 → dist/ec024da790d2972da002.woff2} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
|
@@ -36,6 +36,7 @@ from nautobot.dcim.filters import (
|
|
|
36
36
|
ControllerManagedDeviceGroupFilterSet,
|
|
37
37
|
DeviceBayFilterSet,
|
|
38
38
|
DeviceBayTemplateFilterSet,
|
|
39
|
+
DeviceClusterAssignmentFilterSet,
|
|
39
40
|
DeviceFamilyFilterSet,
|
|
40
41
|
DeviceFilterSet,
|
|
41
42
|
DeviceRedundancyGroupFilterSet,
|
|
@@ -85,6 +86,7 @@ from nautobot.dcim.models import (
|
|
|
85
86
|
Device,
|
|
86
87
|
DeviceBay,
|
|
87
88
|
DeviceBayTemplate,
|
|
89
|
+
DeviceClusterAssignment,
|
|
88
90
|
DeviceFamily,
|
|
89
91
|
DeviceRedundancyGroup,
|
|
90
92
|
DeviceType,
|
|
@@ -122,7 +124,7 @@ from nautobot.dcim.models import (
|
|
|
122
124
|
VirtualChassis,
|
|
123
125
|
VirtualDeviceContext,
|
|
124
126
|
)
|
|
125
|
-
from nautobot.extras.
|
|
127
|
+
from nautobot.extras.filter_mixins import RoleFilter, StatusFilter
|
|
126
128
|
from nautobot.extras.models import ExternalIntegration, Role, SecretsGroup, Status, Tag
|
|
127
129
|
from nautobot.extras.tests.test_customfields_filters import CustomFieldsFilters
|
|
128
130
|
from nautobot.ipam.models import IPAddress, Namespace, Prefix, Service, VLAN, VLANGroup
|
|
@@ -994,13 +996,13 @@ class ModuleDeviceCommonTestsMixin:
|
|
|
994
996
|
"position": 1,
|
|
995
997
|
}
|
|
996
998
|
)
|
|
997
|
-
with self.subTest():
|
|
999
|
+
with self.subTest("has_empty_module_bays: True"):
|
|
998
1000
|
params = {"has_empty_module_bays": True}
|
|
999
1001
|
qs = self.filterset(params, self.queryset).qs
|
|
1000
1002
|
self.assertGreater(qs.count(), 0)
|
|
1001
1003
|
for instance in qs:
|
|
1002
1004
|
self.assertTrue(instance.module_bays.filter(installed_module__isnull=True).exists())
|
|
1003
|
-
with self.subTest():
|
|
1005
|
+
with self.subTest("has_empty_module_bays: False"):
|
|
1004
1006
|
params = {"has_empty_module_bays": False}
|
|
1005
1007
|
qs = self.filterset(params, self.queryset).qs
|
|
1006
1008
|
self.assertGreater(qs.count(), 0)
|
|
@@ -1008,13 +1010,13 @@ class ModuleDeviceCommonTestsMixin:
|
|
|
1008
1010
|
self.assertFalse(instance.module_bays.filter(installed_module__isnull=True).exists())
|
|
1009
1011
|
|
|
1010
1012
|
def test_has_modules(self):
|
|
1011
|
-
with self.subTest():
|
|
1013
|
+
with self.subTest("has_modules: True"):
|
|
1012
1014
|
params = {"has_modules": True}
|
|
1013
1015
|
qs = self.filterset(params, self.queryset).qs
|
|
1014
1016
|
self.assertGreater(qs.count(), 0)
|
|
1015
1017
|
for instance in qs:
|
|
1016
1018
|
self.assertTrue(instance.module_bays.filter(installed_module__isnull=False).exists())
|
|
1017
|
-
with self.subTest():
|
|
1019
|
+
with self.subTest("has_modules: False"):
|
|
1018
1020
|
params = {"has_modules": False}
|
|
1019
1021
|
qs = self.filterset(params, self.queryset).qs
|
|
1020
1022
|
self.assertGreater(qs.count(), 0)
|
|
@@ -1024,13 +1026,13 @@ class ModuleDeviceCommonTestsMixin:
|
|
|
1024
1026
|
|
|
1025
1027
|
class PathEndpointModelTestMixin:
|
|
1026
1028
|
def test_connected(self):
|
|
1027
|
-
with self.subTest():
|
|
1029
|
+
with self.subTest("connected: True"):
|
|
1028
1030
|
params = {"connected": True}
|
|
1029
1031
|
self.assertQuerysetEqualAndNotEmpty(
|
|
1030
1032
|
self.filterset(params, self.queryset).qs,
|
|
1031
1033
|
self.queryset.filter(_path__is_active=True),
|
|
1032
1034
|
)
|
|
1033
|
-
with self.subTest():
|
|
1035
|
+
with self.subTest("connected: False"):
|
|
1034
1036
|
params = {"connected": False}
|
|
1035
1037
|
self.assertQuerysetEqualAndNotEmpty(
|
|
1036
1038
|
self.filterset(params, self.queryset).qs,
|
|
@@ -1060,14 +1062,14 @@ class LocationTypeFilterSetTestCase(
|
|
|
1060
1062
|
cls.lt2.validated_save()
|
|
1061
1063
|
|
|
1062
1064
|
def test_content_types(self):
|
|
1063
|
-
with self.subTest():
|
|
1065
|
+
with self.subTest("rackgroup"):
|
|
1064
1066
|
params = {"content_types": ["dcim.rackgroup"]}
|
|
1065
1067
|
ct = ContentType.objects.get_for_model(RackGroup)
|
|
1066
1068
|
self.assertEqual(
|
|
1067
1069
|
self.filterset(params, self.queryset).qs.count(),
|
|
1068
1070
|
LocationType.objects.filter(content_types=ct).count(),
|
|
1069
1071
|
)
|
|
1070
|
-
with self.subTest():
|
|
1072
|
+
with self.subTest("device and rack"):
|
|
1071
1073
|
params = {"content_types": ["dcim.device", "dcim.rack"]}
|
|
1072
1074
|
ct_1 = [ContentType.objects.get_for_model(Device)]
|
|
1073
1075
|
ct_2 = [ContentType.objects.get_for_model(Rack)]
|
|
@@ -1215,16 +1217,16 @@ class RackGroupTestCase(FilterTestCases.FilterTestCase, CustomFieldsFilters.Cust
|
|
|
1215
1217
|
|
|
1216
1218
|
def test_children(self):
|
|
1217
1219
|
child_groups = RackGroup.objects.filter(name__startswith="Child").filter(parent__isnull=False)[:2]
|
|
1218
|
-
with self.subTest():
|
|
1220
|
+
with self.subTest("2 child groups"):
|
|
1219
1221
|
params = {"children": [child_groups[0].pk, child_groups[1].pk]}
|
|
1220
1222
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
|
1221
|
-
with self.subTest():
|
|
1223
|
+
with self.subTest("repeated group"):
|
|
1222
1224
|
rack_group_4 = RackGroup.objects.filter(name="Rack Group 4").first()
|
|
1223
1225
|
params = {"children": [rack_group_4.pk, rack_group_4.pk]}
|
|
1224
1226
|
self.assertFalse(self.filterset(params, self.queryset).qs.exists())
|
|
1225
1227
|
|
|
1226
1228
|
def test_ancestors(self):
|
|
1227
|
-
with self.subTest():
|
|
1229
|
+
with self.subTest("self.loc3"):
|
|
1228
1230
|
pk_list = []
|
|
1229
1231
|
parent_locations = self.loc3.ancestors(include_self=True)
|
|
1230
1232
|
pk_list.extend([v.pk for v in parent_locations])
|
|
@@ -1232,7 +1234,7 @@ class RackGroupTestCase(FilterTestCases.FilterTestCase, CustomFieldsFilters.Cust
|
|
|
1232
1234
|
expected_queryset = RackGroup.objects.filter(params)
|
|
1233
1235
|
params = {"ancestors": [self.loc3.pk]}
|
|
1234
1236
|
self.assertQuerysetEqualAndNotEmpty(self.filterset(params, self.queryset).qs, expected_queryset)
|
|
1235
|
-
with self.subTest():
|
|
1237
|
+
with self.subTest("self.loc2"):
|
|
1236
1238
|
pk_list = []
|
|
1237
1239
|
parent_locations = self.loc2.ancestors(include_self=True)
|
|
1238
1240
|
pk_list.extend([v.pk for v in parent_locations])
|
|
@@ -1297,19 +1299,19 @@ class RackTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFilter
|
|
|
1297
1299
|
|
|
1298
1300
|
def test_desc_units(self):
|
|
1299
1301
|
# TODO: not a generic_filter_test since this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1300
|
-
with self.subTest():
|
|
1302
|
+
with self.subTest("desc_units: True"):
|
|
1301
1303
|
params = {"desc_units": True}
|
|
1302
1304
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
|
1303
|
-
with self.subTest():
|
|
1305
|
+
with self.subTest("desc_units: False"):
|
|
1304
1306
|
params = {"desc_units": False}
|
|
1305
1307
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
|
1306
1308
|
|
|
1307
1309
|
def test_outer_unit(self):
|
|
1308
1310
|
# TODO: Not a generic_filter_test since this is a single-value filter
|
|
1309
1311
|
# 2.0 TODO: Support filtering for multiple values
|
|
1310
|
-
with self.subTest():
|
|
1312
|
+
with self.subTest("queryset exclude blank"):
|
|
1311
1313
|
self.assertEqual(Rack.objects.exclude(outer_unit="").count(), 3)
|
|
1312
|
-
with self.subTest():
|
|
1314
|
+
with self.subTest("multiple values"):
|
|
1313
1315
|
params = {"outer_unit": [RackDimensionUnitChoices.UNIT_MILLIMETER]}
|
|
1314
1316
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
|
1315
1317
|
|
|
@@ -1446,137 +1448,129 @@ class DeviceTypeTestCase(FilterTestCases.FilterTestCase, CustomFieldsFilters.Cus
|
|
|
1446
1448
|
)
|
|
1447
1449
|
device_type.tags.set(Tag.objects.get_for_model(DeviceType))
|
|
1448
1450
|
|
|
1449
|
-
def
|
|
1451
|
+
def test_special_filters(self):
|
|
1450
1452
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1451
|
-
with self.subTest():
|
|
1453
|
+
with self.subTest("is_full_depth: True"):
|
|
1452
1454
|
params = {"is_full_depth": True}
|
|
1453
1455
|
self.assertQuerysetEqual(
|
|
1454
1456
|
self.filterset(params, self.queryset).qs,
|
|
1455
1457
|
self.queryset.filter(is_full_depth=True),
|
|
1456
1458
|
)
|
|
1457
|
-
with self.subTest():
|
|
1459
|
+
with self.subTest("is_full_depth: False"):
|
|
1458
1460
|
params = {"is_full_depth": False}
|
|
1459
1461
|
self.assertQuerysetEqual(
|
|
1460
1462
|
self.filterset(params, self.queryset).qs,
|
|
1461
1463
|
self.queryset.filter(is_full_depth=False),
|
|
1462
1464
|
)
|
|
1463
1465
|
|
|
1464
|
-
def test_subdevice_role(self):
|
|
1465
1466
|
# TODO: Not a generic_filter_test because this is a single-value filter
|
|
1466
1467
|
# 2.0 TODO: Support filtering for multiple values
|
|
1467
|
-
with self.subTest():
|
|
1468
|
+
with self.subTest("subdevice_role: PARENT"):
|
|
1468
1469
|
params = {"subdevice_role": [SubdeviceRoleChoices.ROLE_PARENT]}
|
|
1469
1470
|
self.assertQuerysetEqual(
|
|
1470
1471
|
self.filterset(params, self.queryset).qs,
|
|
1471
1472
|
self.queryset.filter(subdevice_role=SubdeviceRoleChoices.ROLE_PARENT),
|
|
1472
1473
|
)
|
|
1473
|
-
with self.subTest():
|
|
1474
|
+
with self.subTest("subdevice_role: CHILD"):
|
|
1474
1475
|
params = {"subdevice_role": [SubdeviceRoleChoices.ROLE_CHILD]}
|
|
1475
1476
|
self.assertQuerysetEqual(
|
|
1476
1477
|
self.filterset(params, self.queryset).qs,
|
|
1477
1478
|
self.queryset.filter(subdevice_role=SubdeviceRoleChoices.ROLE_CHILD),
|
|
1478
1479
|
)
|
|
1479
1480
|
|
|
1480
|
-
def test_console_ports(self):
|
|
1481
1481
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1482
|
-
with self.subTest():
|
|
1482
|
+
with self.subTest("console_ports: True"):
|
|
1483
1483
|
params = {"console_ports": True}
|
|
1484
1484
|
self.assertQuerysetEqual(
|
|
1485
1485
|
self.filterset(params, self.queryset).qs,
|
|
1486
1486
|
self.queryset.exclude(console_port_templates__isnull=True),
|
|
1487
1487
|
)
|
|
1488
|
-
with self.subTest():
|
|
1488
|
+
with self.subTest("console_ports: False"):
|
|
1489
1489
|
params = {"console_ports": False}
|
|
1490
1490
|
self.assertQuerysetEqual(
|
|
1491
1491
|
self.filterset(params, self.queryset).qs,
|
|
1492
1492
|
self.queryset.exclude(console_port_templates__isnull=False),
|
|
1493
1493
|
)
|
|
1494
1494
|
|
|
1495
|
-
def test_console_server_ports(self):
|
|
1496
1495
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1497
|
-
with self.subTest():
|
|
1496
|
+
with self.subTest("console_server_ports: True"):
|
|
1498
1497
|
params = {"console_server_ports": True}
|
|
1499
1498
|
self.assertQuerysetEqual(
|
|
1500
1499
|
self.filterset(params, self.queryset).qs,
|
|
1501
1500
|
self.queryset.exclude(console_server_port_templates__isnull=True),
|
|
1502
1501
|
)
|
|
1503
|
-
with self.subTest():
|
|
1502
|
+
with self.subTest("console_server_ports: False"):
|
|
1504
1503
|
params = {"console_server_ports": False}
|
|
1505
1504
|
self.assertQuerysetEqual(
|
|
1506
1505
|
self.filterset(params, self.queryset).qs,
|
|
1507
1506
|
self.queryset.exclude(console_server_port_templates__isnull=False),
|
|
1508
1507
|
)
|
|
1509
1508
|
|
|
1510
|
-
def test_power_ports(self):
|
|
1511
1509
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1512
|
-
with self.subTest():
|
|
1510
|
+
with self.subTest("power_ports: True"):
|
|
1513
1511
|
params = {"power_ports": True}
|
|
1514
1512
|
self.assertQuerysetEqual(
|
|
1515
1513
|
self.filterset(params, self.queryset).qs,
|
|
1516
1514
|
self.queryset.exclude(power_port_templates__isnull=True),
|
|
1517
1515
|
)
|
|
1518
|
-
with self.subTest():
|
|
1516
|
+
with self.subTest("power_ports: False"):
|
|
1519
1517
|
params = {"power_ports": False}
|
|
1520
1518
|
self.assertQuerysetEqual(
|
|
1521
1519
|
self.filterset(params, self.queryset).qs,
|
|
1522
1520
|
self.queryset.exclude(power_port_templates__isnull=False),
|
|
1523
1521
|
)
|
|
1524
1522
|
|
|
1525
|
-
def test_power_outlets(self):
|
|
1526
1523
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1527
|
-
with self.subTest():
|
|
1524
|
+
with self.subTest("power_outlets: True"):
|
|
1528
1525
|
params = {"power_outlets": True}
|
|
1529
1526
|
self.assertQuerysetEqual(
|
|
1530
1527
|
self.filterset(params, self.queryset).qs,
|
|
1531
1528
|
self.queryset.exclude(power_outlet_templates__isnull=True),
|
|
1532
1529
|
)
|
|
1533
|
-
with self.subTest():
|
|
1530
|
+
with self.subTest("power_outlets: False"):
|
|
1534
1531
|
params = {"power_outlets": False}
|
|
1535
1532
|
self.assertQuerysetEqual(
|
|
1536
1533
|
self.filterset(params, self.queryset).qs,
|
|
1537
1534
|
self.queryset.exclude(power_outlet_templates__isnull=False),
|
|
1538
1535
|
)
|
|
1539
1536
|
|
|
1540
|
-
def test_interfaces(self):
|
|
1541
1537
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1542
|
-
with self.subTest():
|
|
1538
|
+
with self.subTest("interfaces: True"):
|
|
1543
1539
|
params = {"interfaces": True}
|
|
1544
1540
|
self.assertQuerysetEqual(
|
|
1545
1541
|
self.filterset(params, self.queryset).qs,
|
|
1546
1542
|
self.queryset.exclude(interface_templates__isnull=True),
|
|
1547
1543
|
)
|
|
1548
|
-
with self.subTest():
|
|
1544
|
+
with self.subTest("interfaces: False"):
|
|
1549
1545
|
params = {"interfaces": False}
|
|
1550
1546
|
self.assertQuerysetEqual(
|
|
1551
1547
|
self.filterset(params, self.queryset).qs,
|
|
1552
1548
|
self.queryset.exclude(interface_templates__isnull=False),
|
|
1553
1549
|
)
|
|
1554
1550
|
|
|
1555
|
-
def test_pass_through_ports(self):
|
|
1556
1551
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1557
1552
|
query = Q(front_port_templates__isnull=False, rear_port_templates__isnull=False)
|
|
1558
|
-
with self.subTest():
|
|
1553
|
+
with self.subTest("pass_through_ports: True"):
|
|
1559
1554
|
params = {"pass_through_ports": True}
|
|
1560
1555
|
self.assertQuerysetEqual(
|
|
1561
1556
|
self.filterset(params, self.queryset).qs,
|
|
1562
1557
|
self.queryset.filter(query).distinct(),
|
|
1563
1558
|
)
|
|
1564
|
-
with self.subTest():
|
|
1559
|
+
with self.subTest("pass_through_ports: False"):
|
|
1565
1560
|
params = {"pass_through_ports": False}
|
|
1566
1561
|
self.assertQuerysetEqual(
|
|
1567
1562
|
self.filterset(params, self.queryset).qs,
|
|
1568
1563
|
self.queryset.filter(~query).distinct(),
|
|
1569
1564
|
)
|
|
1570
1565
|
|
|
1571
|
-
def test_device_bays(self):
|
|
1572
1566
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1573
|
-
with self.subTest():
|
|
1567
|
+
with self.subTest("device_bays: True"):
|
|
1574
1568
|
params = {"device_bays": True}
|
|
1575
1569
|
self.assertQuerysetEqual(
|
|
1576
1570
|
self.filterset(params, self.queryset).qs,
|
|
1577
1571
|
self.queryset.exclude(device_bay_templates__isnull=True),
|
|
1578
1572
|
)
|
|
1579
|
-
with self.subTest():
|
|
1573
|
+
with self.subTest("device_bays: False"):
|
|
1580
1574
|
params = {"device_bays": False}
|
|
1581
1575
|
self.assertQuerysetEqual(
|
|
1582
1576
|
self.filterset(params, self.queryset).qs,
|
|
@@ -1654,13 +1648,13 @@ class InterfaceTemplateTestCase(ModularComponentTemplateTestMixin, FilterTestCas
|
|
|
1654
1648
|
|
|
1655
1649
|
def test_mgmt_only(self):
|
|
1656
1650
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1657
|
-
with self.subTest():
|
|
1651
|
+
with self.subTest("mgmt_only: True"):
|
|
1658
1652
|
params = {"mgmt_only": True}
|
|
1659
1653
|
self.assertQuerysetEqual(
|
|
1660
1654
|
self.filterset(params, self.queryset).qs,
|
|
1661
1655
|
self.queryset.filter(**params),
|
|
1662
1656
|
)
|
|
1663
|
-
with self.subTest():
|
|
1657
|
+
with self.subTest("mgmt_only: False"):
|
|
1664
1658
|
params = {"mgmt_only": False}
|
|
1665
1659
|
self.assertQuerysetEqual(
|
|
1666
1660
|
self.filterset(params, self.queryset).qs,
|
|
@@ -1783,8 +1777,10 @@ class DeviceTestCase(
|
|
|
1783
1777
|
tenancy_related_name = "devices"
|
|
1784
1778
|
generic_filter_tests = [
|
|
1785
1779
|
("asset_tag",),
|
|
1786
|
-
("cluster", "
|
|
1787
|
-
("cluster", "
|
|
1780
|
+
("cluster", "clusters__id"),
|
|
1781
|
+
("cluster", "clusters__name"),
|
|
1782
|
+
("clusters", "clusters__id"),
|
|
1783
|
+
("clusters", "clusters__name"),
|
|
1788
1784
|
("console_ports", "console_ports__id"),
|
|
1789
1785
|
("console_server_ports", "console_server_ports__id"),
|
|
1790
1786
|
("device_bays", "device_bays__id"),
|
|
@@ -1938,101 +1934,96 @@ class DeviceTestCase(
|
|
|
1938
1934
|
virtual_chassis_2 = VirtualChassis.objects.create(name="vc2", master=devices[2])
|
|
1939
1935
|
Device.objects.filter(pk=devices[2].pk).update(virtual_chassis=virtual_chassis_2, vc_position=1, vc_priority=1)
|
|
1940
1936
|
|
|
1941
|
-
def
|
|
1937
|
+
def test_special_filters(self):
|
|
1942
1938
|
# TODO: Not a generic_filter_test because this is a single-value filter
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
self.
|
|
1946
|
-
|
|
1947
|
-
|
|
1939
|
+
with self.subTest("face"):
|
|
1940
|
+
params = {"face": [DeviceFaceChoices.FACE_FRONT]}
|
|
1941
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1942
|
+
self.filterset(params, self.queryset).qs,
|
|
1943
|
+
Device.objects.filter(face=DeviceFaceChoices.FACE_FRONT),
|
|
1944
|
+
)
|
|
1948
1945
|
|
|
1949
|
-
def test_is_full_depth(self):
|
|
1950
1946
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1951
|
-
with self.subTest():
|
|
1947
|
+
with self.subTest("is_full_depth: True"):
|
|
1952
1948
|
params = {"is_full_depth": True}
|
|
1953
1949
|
self.assertQuerysetEqualAndNotEmpty(
|
|
1954
1950
|
self.filterset(params, self.queryset).qs,
|
|
1955
1951
|
Device.objects.filter(device_type__is_full_depth=True),
|
|
1956
1952
|
)
|
|
1957
|
-
with self.subTest():
|
|
1953
|
+
with self.subTest("is_full_depth: False"):
|
|
1958
1954
|
params = {"is_full_depth": False}
|
|
1959
1955
|
self.assertQuerysetEqualAndNotEmpty(
|
|
1960
1956
|
self.filterset(params, self.queryset).qs,
|
|
1961
1957
|
Device.objects.filter(device_type__is_full_depth=False),
|
|
1962
1958
|
)
|
|
1963
1959
|
|
|
1964
|
-
def test_serial(self):
|
|
1965
1960
|
# TODO: Not a generic_filter_test because this is a single-value filter
|
|
1966
1961
|
# 2.0 TODO: Support filtering for multiple values
|
|
1967
|
-
with self.subTest():
|
|
1962
|
+
with self.subTest("serial: uppercase"):
|
|
1968
1963
|
params = {"serial": "ABC"}
|
|
1969
1964
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
|
1970
|
-
with self.subTest():
|
|
1965
|
+
with self.subTest("serial: lowercase"):
|
|
1971
1966
|
params = {"serial": "abc"}
|
|
1972
1967
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
|
1973
1968
|
|
|
1974
|
-
def test_has_primary_ip(self):
|
|
1975
1969
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1976
|
-
with self.subTest():
|
|
1970
|
+
with self.subTest("has_primary_ip: True"):
|
|
1977
1971
|
params = {"has_primary_ip": True}
|
|
1978
1972
|
self.assertQuerysetEqualAndNotEmpty(
|
|
1979
1973
|
self.filterset(params, self.queryset).qs,
|
|
1980
1974
|
Device.objects.filter(Q(primary_ip4__isnull=False) | Q(primary_ip6__isnull=False)),
|
|
1981
1975
|
)
|
|
1982
|
-
with self.subTest():
|
|
1976
|
+
with self.subTest("has_primary_ip: False"):
|
|
1983
1977
|
params = {"has_primary_ip": False}
|
|
1984
1978
|
self.assertQuerysetEqualAndNotEmpty(
|
|
1985
1979
|
self.filterset(params, self.queryset).qs,
|
|
1986
1980
|
Device.objects.filter(primary_ip4__isnull=True, primary_ip6__isnull=True),
|
|
1987
1981
|
)
|
|
1988
1982
|
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1983
|
+
with self.subTest("ip_addresses"):
|
|
1984
|
+
addresses = list(IPAddress.objects.filter(interfaces__isnull=False)[:2])
|
|
1985
|
+
params = {"ip_addresses": [addresses[0].address, addresses[1].id]}
|
|
1986
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1987
|
+
self.filterset(params, self.queryset).qs,
|
|
1988
|
+
self.queryset.filter(interfaces__ip_addresses__in=addresses).distinct(),
|
|
1989
|
+
)
|
|
1996
1990
|
|
|
1997
|
-
def test_virtual_chassis_member(self):
|
|
1998
1991
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
1999
|
-
with self.subTest():
|
|
1992
|
+
with self.subTest("virtual_chassis_member: True"):
|
|
2000
1993
|
params = {"virtual_chassis_member": True}
|
|
2001
1994
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2002
1995
|
self.filterset(params, self.queryset).qs,
|
|
2003
1996
|
Device.objects.filter(virtual_chassis__isnull=False),
|
|
2004
1997
|
)
|
|
2005
|
-
with self.subTest():
|
|
1998
|
+
with self.subTest("virtual_chassis_member: False"):
|
|
2006
1999
|
params = {"virtual_chassis_member": False}
|
|
2007
2000
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2008
2001
|
self.filterset(params, self.queryset).qs,
|
|
2009
2002
|
Device.objects.filter(virtual_chassis__isnull=True),
|
|
2010
2003
|
)
|
|
2011
2004
|
|
|
2012
|
-
def test_is_virtual_chassis_member(self):
|
|
2013
2005
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
2014
|
-
with self.subTest():
|
|
2006
|
+
with self.subTest("is_virtual_chassis_member: True"):
|
|
2015
2007
|
params = {"is_virtual_chassis_member": True}
|
|
2016
2008
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2017
2009
|
self.filterset(params, self.queryset).qs,
|
|
2018
2010
|
Device.objects.filter(virtual_chassis__isnull=False),
|
|
2019
2011
|
)
|
|
2020
|
-
with self.subTest():
|
|
2012
|
+
with self.subTest("is_virtual_chassis_member: False"):
|
|
2021
2013
|
params = {"is_virtual_chassis_member": False}
|
|
2022
2014
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2023
2015
|
self.filterset(params, self.queryset).qs,
|
|
2024
2016
|
Device.objects.filter(virtual_chassis__isnull=True),
|
|
2025
2017
|
)
|
|
2026
2018
|
|
|
2027
|
-
def test_local_config_context_data(self):
|
|
2028
2019
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
2029
|
-
with self.subTest():
|
|
2020
|
+
with self.subTest("local_config_context_data: True"):
|
|
2030
2021
|
params = {"local_config_context_data": True}
|
|
2031
2022
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2032
2023
|
self.filterset(params, self.queryset).qs,
|
|
2033
2024
|
Device.objects.filter(local_config_context_data__isnull=False),
|
|
2034
2025
|
)
|
|
2035
|
-
with self.subTest():
|
|
2026
|
+
with self.subTest("local_config_context_data: False"):
|
|
2036
2027
|
params = {"local_config_context_data": False}
|
|
2037
2028
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2038
2029
|
self.filterset(params, self.queryset).qs,
|
|
@@ -2525,13 +2516,13 @@ class InterfaceTestCase(PathEndpointModelTestMixin, ModularDeviceComponentTestMi
|
|
|
2525
2516
|
|
|
2526
2517
|
def test_enabled(self):
|
|
2527
2518
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
2528
|
-
with self.subTest():
|
|
2519
|
+
with self.subTest("enabled: True"):
|
|
2529
2520
|
params = {"enabled": True}
|
|
2530
2521
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2531
2522
|
self.filterset(params, self.queryset).qs,
|
|
2532
2523
|
self.queryset.filter(**params),
|
|
2533
2524
|
)
|
|
2534
|
-
with self.subTest():
|
|
2525
|
+
with self.subTest("enabled: False"):
|
|
2535
2526
|
params = {"enabled": False}
|
|
2536
2527
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2537
2528
|
self.filterset(params, self.queryset).qs,
|
|
@@ -2540,13 +2531,13 @@ class InterfaceTestCase(PathEndpointModelTestMixin, ModularDeviceComponentTestMi
|
|
|
2540
2531
|
|
|
2541
2532
|
def test_mgmt_only(self):
|
|
2542
2533
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
2543
|
-
with self.subTest():
|
|
2534
|
+
with self.subTest("mgmt_only: True"):
|
|
2544
2535
|
params = {"mgmt_only": True}
|
|
2545
2536
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2546
2537
|
self.filterset(params, self.queryset).qs,
|
|
2547
2538
|
self.queryset.filter(**params),
|
|
2548
2539
|
)
|
|
2549
|
-
with self.subTest():
|
|
2540
|
+
with self.subTest("mgmt_only: False"):
|
|
2550
2541
|
params = {"mgmt_only": False}
|
|
2551
2542
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2552
2543
|
self.filterset(params, self.queryset).qs,
|
|
@@ -2603,17 +2594,17 @@ class InterfaceTestCase(PathEndpointModelTestMixin, ModularDeviceComponentTestMi
|
|
|
2603
2594
|
|
|
2604
2595
|
# Capture the first device so that we can use it in the next test.
|
|
2605
2596
|
device = Device.objects.get(pk=devices[0].pk)
|
|
2606
|
-
with self.subTest():
|
|
2597
|
+
with self.subTest("device_with_common_vc"):
|
|
2607
2598
|
self.assertQuerysetEqual(
|
|
2608
2599
|
queryset,
|
|
2609
2600
|
self.queryset.filter(pk__in=device.common_vc_interfaces.values_list("pk", flat=True)),
|
|
2610
2601
|
)
|
|
2611
2602
|
# Assert interface of a device belonging to same VC as `device` are returned
|
|
2612
|
-
with self.subTest():
|
|
2603
|
+
with self.subTest("interface of a device belonging to same vc as 'device'"):
|
|
2613
2604
|
self.assertTrue(queryset.filter(name="int3").exists())
|
|
2614
2605
|
|
|
2615
2606
|
# Assert interface of a device not belonging as `device` to same VC are not returned
|
|
2616
|
-
with self.subTest():
|
|
2607
|
+
with self.subTest("interface of a device NOT belonging to same vc as 'device'"):
|
|
2617
2608
|
self.assertFalse(queryset.filter(name="int4").exists())
|
|
2618
2609
|
|
|
2619
2610
|
def test_device(self):
|
|
@@ -2767,13 +2758,13 @@ class InterfaceTestCase(PathEndpointModelTestMixin, ModularDeviceComponentTestMi
|
|
|
2767
2758
|
def test_kind(self):
|
|
2768
2759
|
# TODO: Not a generic_filter_test because this is a single-value filter
|
|
2769
2760
|
# 2.0 TODO: Support filtering for multiple values
|
|
2770
|
-
with self.subTest():
|
|
2761
|
+
with self.subTest("kind: physical"):
|
|
2771
2762
|
params = {"kind": "physical"}
|
|
2772
2763
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2773
2764
|
self.filterset(params, self.queryset).qs,
|
|
2774
2765
|
self.queryset.exclude(type__in=NONCONNECTABLE_IFACE_TYPES),
|
|
2775
2766
|
)
|
|
2776
|
-
with self.subTest():
|
|
2767
|
+
with self.subTest("kind: virtual"):
|
|
2777
2768
|
params = {"kind": "virtual"}
|
|
2778
2769
|
self.assertQuerysetEqualAndNotEmpty(
|
|
2779
2770
|
self.filterset(params, self.queryset).qs,
|
|
@@ -3156,20 +3147,20 @@ class InventoryItemTestCase(DeviceComponentTestMixin, FilterTestCases.FilterTest
|
|
|
3156
3147
|
def test_discovered(self):
|
|
3157
3148
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
3158
3149
|
# 2.0 TODO: Fix boolean value
|
|
3159
|
-
with self.subTest():
|
|
3150
|
+
with self.subTest("discovered: True"):
|
|
3160
3151
|
params = {"discovered": True}
|
|
3161
3152
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
|
3162
|
-
with self.subTest():
|
|
3153
|
+
with self.subTest("discovered: False"):
|
|
3163
3154
|
params = {"discovered": False}
|
|
3164
3155
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
|
3165
3156
|
|
|
3166
3157
|
def test_serial(self):
|
|
3167
3158
|
# TODO: Not a generic_filter_test because this is a single-value filter
|
|
3168
3159
|
# 2.0 TODO: Support filtering for multiple values
|
|
3169
|
-
with self.subTest():
|
|
3160
|
+
with self.subTest("serial: uppercase"):
|
|
3170
3161
|
params = {"serial": "ABC"}
|
|
3171
3162
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
|
3172
|
-
with self.subTest():
|
|
3163
|
+
with self.subTest("serial: lowercase"):
|
|
3173
3164
|
params = {"serial": "abc"}
|
|
3174
3165
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
|
3175
3166
|
|
|
@@ -3541,17 +3532,17 @@ class CableTestCase(FilterTestCases.FilterTestCase):
|
|
|
3541
3532
|
def test_rack(self):
|
|
3542
3533
|
# TODO: Not a generic_filter_test because this is a method filter.
|
|
3543
3534
|
racks = Rack.objects.all()[:2]
|
|
3544
|
-
with self.subTest():
|
|
3535
|
+
with self.subTest("rack_id"):
|
|
3545
3536
|
params = {"rack_id": [racks[0].pk, racks[1].pk]}
|
|
3546
3537
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
|
3547
|
-
with self.subTest():
|
|
3538
|
+
with self.subTest("rack"):
|
|
3548
3539
|
params = {"rack": [racks[0].name, racks[1].name]}
|
|
3549
3540
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
|
3550
3541
|
|
|
3551
3542
|
def test_tenant(self):
|
|
3552
3543
|
# TODO: Not a generic_filter_test because this is a method filter.
|
|
3553
3544
|
tenants = list(Tenant.objects.filter(devices__isnull=False))[:2]
|
|
3554
|
-
with self.subTest():
|
|
3545
|
+
with self.subTest("tenant_id"):
|
|
3555
3546
|
params = {"tenant_id": [tenants[0].pk, tenants[1].pk]}
|
|
3556
3547
|
self.assertQuerysetEqual(
|
|
3557
3548
|
self.filterset(params, self.queryset).qs,
|
|
@@ -3559,7 +3550,7 @@ class CableTestCase(FilterTestCases.FilterTestCase):
|
|
|
3559
3550
|
Q(_termination_a_device__tenant__in=tenants) | Q(_termination_b_device__tenant__in=tenants)
|
|
3560
3551
|
),
|
|
3561
3552
|
)
|
|
3562
|
-
with self.subTest():
|
|
3553
|
+
with self.subTest("tenant"):
|
|
3563
3554
|
params = {"tenant": [tenants[0].name, tenants[1].name]}
|
|
3564
3555
|
self.assertQuerysetEqual(
|
|
3565
3556
|
self.filterset(params, self.queryset).qs,
|
|
@@ -3574,22 +3565,22 @@ class CableTestCase(FilterTestCases.FilterTestCase):
|
|
|
3574
3565
|
type_interface = "dcim.interface"
|
|
3575
3566
|
type_console_port = "dcim.consoleport"
|
|
3576
3567
|
type_console_server_port = "dcim.consoleserverport"
|
|
3577
|
-
with self.subTest():
|
|
3568
|
+
with self.subTest("termination_a_type: interface, console_port"):
|
|
3578
3569
|
params = {"termination_a_type": [type_interface, type_console_port]}
|
|
3579
3570
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
|
|
3580
|
-
with self.subTest():
|
|
3571
|
+
with self.subTest("termination_a_type: interface"):
|
|
3581
3572
|
params = {"termination_a_type": [type_interface]}
|
|
3582
3573
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
|
|
3583
|
-
with self.subTest():
|
|
3574
|
+
with self.subTest("termination_b_type: interface, console_server_port"):
|
|
3584
3575
|
params = {"termination_b_type": [type_interface, type_console_server_port]}
|
|
3585
3576
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
|
|
3586
|
-
with self.subTest():
|
|
3577
|
+
with self.subTest("termination_b_type: interface"):
|
|
3587
3578
|
params = {"termination_b_type": [type_interface]}
|
|
3588
3579
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
|
|
3589
|
-
with self.subTest():
|
|
3580
|
+
with self.subTest("termination_type: interface"):
|
|
3590
3581
|
params = {"termination_type": [type_interface]}
|
|
3591
3582
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
|
|
3592
|
-
with self.subTest():
|
|
3583
|
+
with self.subTest("termination_type: console_port, console_server_port"):
|
|
3593
3584
|
params = {"termination_type": [type_console_port, type_console_server_port]}
|
|
3594
3585
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
|
3595
3586
|
|
|
@@ -3785,13 +3776,13 @@ class DeviceRedundancyGroupTestCase(FilterTestCases.FilterTestCase):
|
|
|
3785
3776
|
def test_failover_strategy(self):
|
|
3786
3777
|
# TODO: Not a generic_filter_test because this is a single-value filter
|
|
3787
3778
|
# 2.0 TODO: Support filtering for multiple values
|
|
3788
|
-
with self.subTest():
|
|
3779
|
+
with self.subTest("failover_strategy: active-active"):
|
|
3789
3780
|
params = {"failover_strategy": ["active-active"]}
|
|
3790
3781
|
self.assertQuerysetEqualAndNotEmpty(
|
|
3791
3782
|
self.filterset(params, self.queryset).qs,
|
|
3792
3783
|
DeviceRedundancyGroup.objects.filter(failover_strategy="active-active"),
|
|
3793
3784
|
)
|
|
3794
|
-
with self.subTest():
|
|
3785
|
+
with self.subTest("failover_strategy: active-passive"):
|
|
3795
3786
|
params = {"failover_strategy": ["active-passive"]}
|
|
3796
3787
|
self.assertQuerysetEqualAndNotEmpty(
|
|
3797
3788
|
self.filterset(params, self.queryset).qs,
|
|
@@ -3865,13 +3856,13 @@ class InterfaceRedundancyGroupTestCase(FilterTestCases.FilterTestCase):
|
|
|
3865
3856
|
interface_redundancy_groups[1].validated_save()
|
|
3866
3857
|
|
|
3867
3858
|
def test_virtual_ip(self):
|
|
3868
|
-
with self.subTest():
|
|
3859
|
+
with self.subTest("virtual_ip: pks"):
|
|
3869
3860
|
params = {"virtual_ip": [self.ips[0].pk, self.ips[1].pk]}
|
|
3870
3861
|
self.assertQuerysetEqualAndNotEmpty(
|
|
3871
3862
|
self.filterset(params, self.queryset).qs,
|
|
3872
3863
|
InterfaceRedundancyGroup.objects.filter(virtual_ip__in=params["virtual_ip"]),
|
|
3873
3864
|
)
|
|
3874
|
-
with self.subTest():
|
|
3865
|
+
with self.subTest("virtual_ip: addresses"):
|
|
3875
3866
|
params = {"virtual_ip": [str(self.ips[2].address), str(self.ips[3].address)]}
|
|
3876
3867
|
self.assertQuerysetEqualAndNotEmpty(
|
|
3877
3868
|
self.filterset(params, self.queryset).qs,
|
|
@@ -4239,13 +4230,13 @@ class ModuleBayTemplateTestCase(FilterTestCases.FilterTestCase):
|
|
|
4239
4230
|
|
|
4240
4231
|
def test_requires_first_party_modules(self):
|
|
4241
4232
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
4242
|
-
with self.subTest():
|
|
4233
|
+
with self.subTest("requires_first_party_modules: True"):
|
|
4243
4234
|
params = {"requires_first_party_modules": True}
|
|
4244
4235
|
self.assertQuerysetEqual(
|
|
4245
4236
|
self.filterset(params, self.queryset).qs,
|
|
4246
4237
|
self.queryset.filter(requires_first_party_modules=True),
|
|
4247
4238
|
)
|
|
4248
|
-
with self.subTest():
|
|
4239
|
+
with self.subTest("requires_first_party_modules: False"):
|
|
4249
4240
|
params = {"requires_first_party_modules": False}
|
|
4250
4241
|
self.assertQuerysetEqual(
|
|
4251
4242
|
self.filterset(params, self.queryset).qs,
|
|
@@ -4335,13 +4326,13 @@ class VirtualDeviceContextTestCase(FilterTestCases.FilterTestCase, FilterTestCas
|
|
|
4335
4326
|
|
|
4336
4327
|
def test_has_primary_ip(self):
|
|
4337
4328
|
# TODO: Not a generic_filter_test because this is a boolean filter but not a RelatedMembershipBooleanFilter
|
|
4338
|
-
with self.subTest():
|
|
4329
|
+
with self.subTest("has_primary_ip: True"):
|
|
4339
4330
|
params = {"has_primary_ip": True}
|
|
4340
4331
|
self.assertQuerysetEqualAndNotEmpty(
|
|
4341
4332
|
self.filterset(params, self.queryset).qs,
|
|
4342
4333
|
VirtualDeviceContext.objects.filter(Q(primary_ip4__isnull=False) | Q(primary_ip6__isnull=False)),
|
|
4343
4334
|
)
|
|
4344
|
-
with self.subTest():
|
|
4335
|
+
with self.subTest("has_primary_ip: False"):
|
|
4345
4336
|
params = {"has_primary_ip": False}
|
|
4346
4337
|
self.assertQuerysetEqualAndNotEmpty(
|
|
4347
4338
|
self.filterset(params, self.queryset).qs,
|
|
@@ -4451,3 +4442,61 @@ class ModuleFamilyTestCase(FilterTestCases.FilterTestCase):
|
|
|
4451
4442
|
manufacturer=manufacturers[1], model="Model 2", module_family=cls.module_families[1]
|
|
4452
4443
|
),
|
|
4453
4444
|
)
|
|
4445
|
+
|
|
4446
|
+
|
|
4447
|
+
class DeviceClusterAssignmentTestCase(FilterTestCases.FilterTestCase):
|
|
4448
|
+
queryset = DeviceClusterAssignment.objects.all()
|
|
4449
|
+
filterset = DeviceClusterAssignmentFilterSet
|
|
4450
|
+
generic_filter_tests = [
|
|
4451
|
+
("device", "device__id"),
|
|
4452
|
+
("device", "device__name"),
|
|
4453
|
+
("cluster", "cluster__id"),
|
|
4454
|
+
("cluster", "cluster__name"),
|
|
4455
|
+
]
|
|
4456
|
+
|
|
4457
|
+
@classmethod
|
|
4458
|
+
def setUpTestData(cls):
|
|
4459
|
+
manufacturer = Manufacturer.objects.first()
|
|
4460
|
+
device_type = DeviceType.objects.create(
|
|
4461
|
+
manufacturer=manufacturer,
|
|
4462
|
+
model="Test Device Type Filter 1",
|
|
4463
|
+
)
|
|
4464
|
+
device_role = Role.objects.get_for_model(Device).first()
|
|
4465
|
+
device_status = Status.objects.get_for_model(Device).first()
|
|
4466
|
+
location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
|
|
4467
|
+
devices = (
|
|
4468
|
+
Device.objects.create(
|
|
4469
|
+
device_type=device_type,
|
|
4470
|
+
role=device_role,
|
|
4471
|
+
name="Test Device Filter 1",
|
|
4472
|
+
location=location,
|
|
4473
|
+
status=device_status,
|
|
4474
|
+
),
|
|
4475
|
+
Device.objects.create(
|
|
4476
|
+
device_type=device_type,
|
|
4477
|
+
role=device_role,
|
|
4478
|
+
name="Test Device Filter 2",
|
|
4479
|
+
location=location,
|
|
4480
|
+
status=device_status,
|
|
4481
|
+
),
|
|
4482
|
+
Device.objects.create(
|
|
4483
|
+
device_type=device_type,
|
|
4484
|
+
role=device_role,
|
|
4485
|
+
name="Test Device Filter 3",
|
|
4486
|
+
location=location,
|
|
4487
|
+
status=device_status,
|
|
4488
|
+
),
|
|
4489
|
+
)
|
|
4490
|
+
cluster_type = ClusterType.objects.create(name="Test Cluster Type Filter")
|
|
4491
|
+
clusters = (
|
|
4492
|
+
Cluster.objects.create(name="Test Cluster Filter 1", cluster_type=cluster_type),
|
|
4493
|
+
Cluster.objects.create(name="Test Cluster Filter 2", cluster_type=cluster_type),
|
|
4494
|
+
Cluster.objects.create(name="Test Cluster Filter 3", cluster_type=cluster_type),
|
|
4495
|
+
)
|
|
4496
|
+
cls.device_cluster_assignments = (
|
|
4497
|
+
DeviceClusterAssignment.objects.create(device=devices[0], cluster=clusters[0]),
|
|
4498
|
+
DeviceClusterAssignment.objects.create(device=devices[1], cluster=clusters[1]),
|
|
4499
|
+
DeviceClusterAssignment.objects.create(device=devices[0], cluster=clusters[1]),
|
|
4500
|
+
DeviceClusterAssignment.objects.create(device=devices[1], cluster=clusters[2]),
|
|
4501
|
+
DeviceClusterAssignment.objects.create(device=devices[2], cluster=clusters[0]),
|
|
4502
|
+
)
|