nautobot 3.0.0a2__py3-none-any.whl → 3.0.0rc1__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.
- nautobot/apps/choices.py +4 -2
- nautobot/apps/filters.py +7 -9
- nautobot/apps/models.py +2 -2
- nautobot/apps/ui.py +13 -1
- nautobot/apps/utils.py +8 -0
- nautobot/circuits/filters.py +3 -2
- nautobot/circuits/navigation.py +3 -2
- nautobot/circuits/templates/circuits/circuit_create.html +3 -3
- nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
- nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
- nautobot/circuits/tests/integration/test_circuit.py +10 -13
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
- nautobot/circuits/views.py +6 -2
- nautobot/cloud/filters.py +1 -1
- nautobot/cloud/navigation.py +3 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +6 -1
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +12 -0
- nautobot/core/apps/__init__.py +11 -10
- nautobot/core/celery/__init__.py +3 -5
- nautobot/core/checks.py +46 -0
- nautobot/core/choices.py +1 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +105 -13
- nautobot/core/cli/migrate_deprecated_templates.py +227 -0
- nautobot/core/constants.py +3 -0
- nautobot/core/context_processors.py +9 -1
- nautobot/core/filters.py +4 -0
- nautobot/core/forms/__init__.py +2 -0
- nautobot/core/forms/forms.py +1 -1
- nautobot/core/forms/widgets.py +21 -2
- nautobot/core/jobs/__init__.py +62 -3
- nautobot/core/jobs/groups.py +31 -1
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +11 -0
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/tree_queries.py +10 -5
- nautobot/core/models/utils.py +1 -1
- nautobot/core/settings.py +35 -19
- nautobot/core/settings.yaml +17 -33
- nautobot/core/signals.py +12 -1
- nautobot/core/tables.py +13 -6
- nautobot/core/templates/40x.html +1 -1
- nautobot/core/templates/500.html +2 -2
- nautobot/core/templates/admin/base.html +1 -2
- nautobot/core/templates/admin/change_list.html +9 -12
- nautobot/core/templates/admin/config/config.html +12 -12
- nautobot/core/templates/admin/index.html +3 -3
- nautobot/core/templates/base_django.html +1 -2
- nautobot/core/templates/buttons/export.html +1 -1
- nautobot/core/templates/components/button/dropdown.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
- nautobot/core/templates/components/panel/panel.html +3 -3
- nautobot/core/templates/components/tab/content_wrapper.html +6 -7
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
- nautobot/core/templates/echarts/echarts.html +22 -9
- nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
- nautobot/core/templates/generic/object_bulk_create.html +6 -5
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
- nautobot/core/templates/generic/object_bulk_edit.html +1 -1
- nautobot/core/templates/generic/object_bulk_import.html +1 -1
- nautobot/core/templates/generic/object_bulk_remove.html +2 -2
- nautobot/core/templates/generic/object_bulk_update.html +5 -4
- nautobot/core/templates/generic/object_create.html +5 -4
- nautobot/core/templates/generic/object_delete.html +1 -1
- nautobot/core/templates/generic/object_detail.html +1 -1
- nautobot/core/templates/generic/object_edit.html +1 -1
- nautobot/core/templates/generic/object_import.html +2 -1
- nautobot/core/templates/generic/object_list.html +12 -4
- nautobot/core/templates/generic/object_notes.html +5 -3
- nautobot/core/templates/generic/object_retrieve.html +4 -5
- nautobot/core/templates/graphene/graphiql.html +7 -8
- nautobot/core/templates/home.html +1 -1
- nautobot/core/templates/import_success.html +2 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/created_updated.html +7 -3
- nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/footer.html +3 -1
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +11 -1
- nautobot/core/templates/inc/image_attachments.html +2 -1
- nautobot/core/templates/inc/media.html +14 -0
- nautobot/core/templates/inc/nav_menu.html +3 -9
- nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
- nautobot/core/templates/inc/search_panel.html +4 -4
- nautobot/core/templates/login.html +4 -2
- nautobot/core/templates/nautobot_config.py.j2 +6 -11
- nautobot/core/templates/redoc_ui.html +7 -0
- nautobot/core/templates/rest_framework/api.html +103 -2
- nautobot/core/templates/search.html +1 -1
- nautobot/core/templates/swagger_ui.html +17 -3
- nautobot/core/templates/system_jobs/import_objects.html +1 -2
- nautobot/core/templates/utilities/confirmation_form.html +2 -2
- nautobot/core/templates/utilities/obj_table.html +10 -2
- nautobot/core/templates/utilities/render_field.html +7 -7
- nautobot/core/templates/utilities/render_jinja2.html +2 -2
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +37 -4
- nautobot/core/templates/utilities/theme_preview.html +19 -3
- nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templatetags/helpers.py +76 -18
- nautobot/core/testing/api.py +68 -9
- nautobot/core/testing/filters.py +0 -23
- nautobot/core/testing/integration.py +41 -17
- nautobot/core/testing/mixins.py +2 -0
- nautobot/core/testing/utils.py +18 -4
- nautobot/core/testing/views.py +104 -13
- nautobot/core/tests/integration/test_app_home.py +34 -30
- nautobot/core/tests/integration/test_app_navbar.py +3 -0
- nautobot/core/tests/integration/test_filters.py +48 -11
- nautobot/core/tests/integration/test_theme.py +22 -21
- nautobot/core/tests/nautobot_config.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +8 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_breadcrumbs.py +27 -28
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_cli.py +40 -0
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_forms.py +55 -13
- nautobot/core/tests/test_jobs.py +144 -3
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +76 -1
- nautobot/core/tests/test_patch_social_django.py +42 -0
- nautobot/core/tests/test_renderers.py +59 -0
- nautobot/core/tests/test_settings_schema.py +1 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +62 -13
- nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
- nautobot/core/tests/test_titles.py +0 -16
- nautobot/core/tests/test_tree_queries.py +14 -1
- nautobot/core/tests/test_ui.py +123 -4
- nautobot/core/tests/test_utils.py +72 -5
- nautobot/core/tests/test_views.py +159 -31
- nautobot/core/ui/breadcrumbs.py +70 -29
- nautobot/core/ui/bulk_buttons.py +1 -1
- nautobot/core/ui/choices.py +143 -27
- nautobot/core/ui/constants.py +76 -12
- nautobot/core/ui/echarts.py +15 -20
- nautobot/core/ui/object_detail.py +143 -55
- nautobot/core/ui/titles.py +3 -6
- nautobot/core/urls.py +20 -9
- nautobot/core/utils/cache.py +2 -1
- nautobot/core/utils/filtering.py +28 -18
- nautobot/core/utils/lookup.py +49 -8
- nautobot/core/utils/module_loading.py +21 -0
- nautobot/core/utils/patch_social_django.py +128 -0
- nautobot/core/views/__init__.py +38 -1
- nautobot/core/views/generic.py +3 -3
- nautobot/core/views/mixins.py +45 -22
- nautobot/core/views/renderers.py +4 -3
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/apps.py +1 -5
- nautobot/data_validation/custom_validators.py +4 -4
- nautobot/data_validation/filters.py +1 -1
- nautobot/data_validation/forms.py +40 -0
- nautobot/data_validation/migrations/0001_initial.py +0 -7
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -14
- nautobot/data_validation/models.py +16 -7
- nautobot/data_validation/navigation.py +8 -1
- nautobot/data_validation/tables.py +12 -5
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +2 -2
- nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
- nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
- nautobot/data_validation/tests/test_filters.py +8 -6
- nautobot/data_validation/tests/test_models.py +15 -0
- nautobot/data_validation/tests/test_views.py +190 -32
- nautobot/data_validation/urls.py +2 -5
- nautobot/data_validation/views.py +73 -40
- nautobot/dcim/api/serializers.py +3 -13
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +65 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +15 -36
- nautobot/dcim/forms.py +90 -4
- nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
- nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
- nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
- nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
- nautobot/dcim/migrations/0079_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0080_device_name_data_migration.py +59 -0
- nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
- nautobot/dcim/models/device_component_templates.py +33 -1
- nautobot/dcim/models/device_components.py +98 -64
- nautobot/dcim/models/devices.py +30 -20
- nautobot/dcim/navigation.py +7 -6
- nautobot/dcim/tables/devices.py +18 -0
- nautobot/dcim/tables/devicetypes.py +8 -1
- nautobot/dcim/tables/racks.py +0 -2
- nautobot/dcim/tables/template_code.py +15 -15
- nautobot/dcim/templates/dcim/cable_connect.html +28 -112
- nautobot/dcim/templates/dcim/cable_trace.html +0 -4
- nautobot/dcim/templates/dcim/{cable_edit.html → cable_update.html} +1 -1
- nautobot/dcim/templates/dcim/consoleport.html +7 -6
- nautobot/dcim/templates/dcim/consoleserverport.html +7 -6
- nautobot/dcim/templates/dcim/device/config.html +2 -2
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
- nautobot/dcim/templates/dcim/device/status.html +8 -8
- nautobot/dcim/templates/dcim/device.html +1 -1
- nautobot/dcim/templates/dcim/device_component_add.html +2 -2
- nautobot/dcim/templates/dcim/device_create.html +5 -3
- nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
- nautobot/dcim/templates/dcim/device_list.html +73 -10
- nautobot/dcim/templates/dcim/devicebay.html +1 -1
- nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
- nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
- nautobot/dcim/templates/dcim/frontport.html +10 -9
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
- nautobot/dcim/templates/dcim/interface.html +35 -7
- nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/interface_edit.html +2 -0
- nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
- nautobot/dcim/templates/dcim/module/base.html +49 -9
- nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
- nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
- nautobot/dcim/templates/dcim/module_frontports.html +1 -1
- nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
- nautobot/dcim/templates/dcim/module_list.html +57 -8
- nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
- nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
- nautobot/dcim/templates/dcim/module_powerports.html +1 -1
- nautobot/dcim/templates/dcim/module_rearports.html +1 -1
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
- nautobot/dcim/templates/dcim/platform_create.html +1 -1
- nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +6 -5
- nautobot/dcim/templates/dcim/rack_elevation_list.html +17 -5
- nautobot/dcim/templates/dcim/rack_retrieve.html +22 -15
- nautobot/dcim/templates/dcim/rearport.html +8 -7
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
- nautobot/dcim/templates/dcim/virtualchassis_update.html +15 -7
- nautobot/dcim/tests/integration/test_controller.py +4 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
- nautobot/dcim/tests/integration/test_create_device.py +0 -2
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
- nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
- nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
- nautobot/dcim/tests/test_api.py +194 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +55 -7
- nautobot/dcim/tests/test_forms.py +110 -8
- nautobot/dcim/tests/test_graphql.py +44 -1
- nautobot/dcim/tests/test_models.py +328 -4
- nautobot/dcim/tests/test_tables.py +160 -0
- nautobot/dcim/tests/test_views.py +132 -29
- nautobot/dcim/urls.py +64 -21
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +777 -397
- nautobot/extras/api/views.py +60 -45
- nautobot/extras/choices.py +2 -13
- nautobot/extras/datasources/git.py +3 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
- nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
- nautobot/extras/{filters/__init__.py → filters.py} +33 -48
- nautobot/extras/forms/forms.py +14 -15
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/jobs.py +2 -0
- nautobot/extras/jobs_ui.py +4 -3
- nautobot/extras/management/__init__.py +11 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
- nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
- 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/migrations/0131_configcontext_device_families.py +18 -0
- nautobot/extras/models/__init__.py +1 -2
- nautobot/extras/models/approvals.py +33 -14
- nautobot/extras/models/change_logging.py +4 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/groups.py +44 -5
- nautobot/extras/models/jobs.py +60 -4
- nautobot/extras/models/mixins.py +28 -0
- nautobot/extras/models/models.py +23 -2
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/navigation.py +13 -9
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +49 -1
- nautobot/extras/plugins/tables.py +3 -3
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/views.py +0 -9
- nautobot/extras/querysets.py +8 -0
- nautobot/extras/signals.py +20 -19
- nautobot/extras/tables.py +64 -68
- nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
- nautobot/extras/templates/extras/approval_dashboard.html +7 -5
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
- nautobot/extras/templates/extras/configcontext_update.html +1 -0
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
- nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +0 -2
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
- nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
- nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
- nautobot/extras/templates/extras/inc/json_format.html +10 -3
- nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
- nautobot/extras/templates/extras/job.html +29 -12
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
- nautobot/extras/templates/extras/job_edit.html +52 -46
- nautobot/extras/templates/extras/job_list.html +29 -25
- nautobot/extras/templates/extras/marketplace.html +5 -9
- nautobot/extras/templates/extras/object_configcontext.html +1 -1
- nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
- nautobot/extras/templates/extras/objectchange_retrieve.html +19 -39
- nautobot/extras/templates/extras/plugin_detail.html +29 -24
- nautobot/extras/templates/extras/plugins_list.html +16 -26
- nautobot/extras/templates/extras/role_retrieve.html +64 -0
- nautobot/extras/templates/extras/scheduledjob.html +4 -2
- nautobot/extras/templates/extras/secret_create.html +1 -1
- 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 +8 -9
- nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
- nautobot/extras/tests/integration/test_customfields.py +9 -10
- nautobot/extras/tests/integration/test_dynamicgroups.py +12 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +18 -6
- nautobot/extras/tests/integration/test_relationships.py +0 -2
- nautobot/extras/tests/test_api.py +90 -18
- nautobot/extras/tests/test_approvals.py +38 -38
- nautobot/extras/tests/test_changelog.py +59 -5
- nautobot/extras/tests/test_customfields.py +22 -13
- nautobot/extras/tests/test_customfields_filters.py +479 -0
- nautobot/extras/tests/test_dynamicgroups.py +39 -1
- nautobot/extras/tests/test_filters.py +57 -22
- nautobot/extras/tests/test_forms.py +18 -21
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +1 -0
- nautobot/extras/tests/test_models.py +51 -33
- nautobot/extras/tests/test_plugins.py +36 -10
- nautobot/extras/tests/test_utils.py +3 -4
- nautobot/extras/tests/test_views.py +52 -112
- nautobot/extras/urls.py +0 -14
- nautobot/extras/views.py +164 -71
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +53 -38
- 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 -2
- nautobot/ipam/signals.py +71 -0
- nautobot/ipam/tables.py +19 -6
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
- nautobot/ipam/templates/ipam/ipaddress.html +14 -0
- nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
- 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_delete.html +1 -1
- nautobot/ipam/templates/ipam/prefix_list.html +14 -13
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +13 -6
- nautobot/ipam/tests/test_filters.py +36 -1
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +44 -2
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +13 -14
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +53 -11
- nautobot/load_balancers/__init__.py +0 -0
- nautobot/load_balancers/api/__init__.py +1 -0
- nautobot/load_balancers/api/serializers.py +75 -0
- nautobot/load_balancers/api/urls.py +23 -0
- nautobot/load_balancers/api/views.py +61 -0
- nautobot/load_balancers/apps.py +17 -0
- nautobot/load_balancers/choices.py +167 -0
- nautobot/load_balancers/filters.py +225 -0
- nautobot/load_balancers/forms.py +532 -0
- nautobot/load_balancers/management/commands/__init__.py +0 -0
- nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
- nautobot/load_balancers/migrations/0001_initial.py +465 -0
- nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
- nautobot/load_balancers/migrations/__init__.py +0 -0
- nautobot/load_balancers/models.py +423 -0
- nautobot/load_balancers/navigation.py +80 -0
- nautobot/load_balancers/tables.py +255 -0
- nautobot/load_balancers/tests/__init__.py +474 -0
- nautobot/load_balancers/tests/test_api.py +353 -0
- nautobot/load_balancers/tests/test_filters.py +134 -0
- nautobot/load_balancers/tests/test_forms.py +266 -0
- nautobot/load_balancers/tests/test_models.py +195 -0
- nautobot/load_balancers/tests/test_views.py +229 -0
- nautobot/load_balancers/urls.py +17 -0
- nautobot/load_balancers/views.py +248 -0
- nautobot/project-static/dist/css/github-dark.min.css +10 -0
- nautobot/project-static/dist/css/github.min.css +10 -0
- nautobot/project-static/dist/css/nautobot.css +1 -11
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/libraries.js +1 -1
- nautobot/project-static/dist/js/libraries.js.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/forms.js +13 -0
- nautobot/project-static/js/interface_filtering.js +20 -16
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +1 -1
- nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
- nautobot/project-static/nautobot-icons/elements.svg +1 -1
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +1 -1
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +1 -1
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +1 -1
- nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
- nautobot/project-static/nautobot-icons/star.svg +1 -1
- 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/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/package-lock.json +87 -4
- nautobot/ui/package.json +2 -1
- nautobot/ui/src/js/collapse.js +3 -3
- nautobot/ui/src/js/nautobot.js +16 -1
- nautobot/ui/src/js/select2.js +53 -2
- nautobot/ui/src/scss/colors.scss +1 -1
- nautobot/ui/src/scss/nautobot.scss +112 -30
- nautobot/ui/webpack.config.js +13 -0
- nautobot/users/templates/users/preferences.html +11 -2
- nautobot/users/templates/users/profile.html +45 -12
- nautobot/users/templates/users/sessionkey_delete.html +1 -1
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/views.py +4 -2
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/models.py +1 -68
- nautobot/virtualization/navigation.py +3 -2
- nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_filters.py +10 -1
- nautobot/virtualization/tests/test_models.py +45 -4
- nautobot/virtualization/views.py +4 -1
- 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 +219 -0
- nautobot/vpn/filters.py +234 -0
- nautobot/vpn/forms.py +487 -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 +535 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +383 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +336 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +293 -0
- nautobot/vpn/tests/test_models.py +147 -0
- nautobot/vpn/tests/test_views.py +300 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +495 -0
- nautobot/wireless/navigation.py +3 -2
- nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +15 -15
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +514 -572
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +1 -0
- nautobot/circuits/templates/circuits/circuit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuit_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittermination.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittype.html +0 -2
- nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
- nautobot/circuits/templates/circuits/provider.html +0 -2
- nautobot/circuits/templates/circuits/provider_edit.html +0 -2
- nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
- nautobot/circuits/templates/circuits/provider_update.html +0 -1
- nautobot/circuits/templates/circuits/providernetwork.html +0 -2
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
- nautobot/core/templates/buttons/import.html +0 -9
- nautobot/data_validation/template_content.py +0 -42
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/dcim/templates/dcim/controller/base.html +0 -2
- nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/device/base.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
- nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
- nautobot/dcim/templates/dcim/device/frontports.html +0 -2
- nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
- nautobot/dcim/templates/dcim/device/inventory.html +0 -2
- nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
- nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
- nautobot/dcim/templates/dcim/device/powerports.html +0 -2
- nautobot/dcim/templates/dcim/device/rearports.html +0 -2
- nautobot/dcim/templates/dcim/device/wireless.html +0 -2
- nautobot/dcim/templates/dcim/device_component.html +0 -2
- nautobot/dcim/templates/dcim/device_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/devicetype.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
- nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/location.html +0 -2
- nautobot/dcim/templates/dcim/location_edit.html +0 -2
- nautobot/dcim/templates/dcim/location_retrieve.html +0 -243
- nautobot/dcim/templates/dcim/locationtype.html +0 -2
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/manufacturer.html +0 -2
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
- nautobot/dcim/templates/dcim/platform.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/rack.html +0 -2
- nautobot/dcim/templates/dcim/rack_edit.html +0 -2
- nautobot/dcim/templates/dcim/rackgroup.html +0 -2
- nautobot/dcim/templates/dcim/rackreservation.html +0 -2
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
- nautobot/dcim/ui.py +0 -29
- nautobot/extras/templates/extras/computedfield.html +0 -2
- nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontext.html +0 -2
- nautobot/extras/templates/extras/configcontext_edit.html +0 -2
- nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontextschema.html +0 -2
- nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
- nautobot/extras/templates/extras/contact_retrieve.html +0 -2
- nautobot/extras/templates/extras/customfield.html +0 -2
- nautobot/extras/templates/extras/customfield_edit.html +0 -2
- nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/customlink.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
- nautobot/extras/templates/extras/exporttemplate.html +0 -2
- nautobot/extras/templates/extras/gitrepository.html +0 -2
- nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
- nautobot/extras/templates/extras/graphqlquery.html +0 -2
- nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -97
- nautobot/extras/templates/extras/job_detail.html +0 -2
- nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobhook.html +0 -2
- nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobresult.html +0 -2
- nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
- nautobot/extras/templates/extras/note.html +0 -2
- nautobot/extras/templates/extras/note_retrieve.html +0 -1
- nautobot/extras/templates/extras/object_changelog.html +0 -2
- nautobot/extras/templates/extras/object_notes.html +0 -2
- nautobot/extras/templates/extras/objectchange.html +0 -2
- nautobot/extras/templates/extras/objectchange_list.html +0 -3
- nautobot/extras/templates/extras/relationship.html +0 -1
- nautobot/extras/templates/extras/secret.html +0 -1
- nautobot/extras/templates/extras/secret_edit.html +0 -1
- nautobot/extras/templates/extras/secretsgroup.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
- nautobot/extras/templates/extras/status.html +0 -2
- nautobot/extras/templates/extras/tag.html +0 -2
- nautobot/extras/templates/extras/tag_edit.html +0 -2
- nautobot/extras/templates/extras/tag_retrieve.html +0 -2
- nautobot/extras/templates/extras/team_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/prefix.html +0 -2
- nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/rir.html +0 -2
- nautobot/ipam/templates/ipam/routetarget.html +0 -1
- nautobot/ipam/templates/ipam/service.html +0 -2
- nautobot/ipam/templates/ipam/service_edit.html +0 -2
- nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlan.html +0 -2
- nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
- nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlangroup.html +0 -2
- nautobot/ipam/templates/ipam/vrf.html +0 -1
- nautobot/tenancy/templates/tenancy/tenant.html +0 -2
- nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
- /nautobot/dcim/templates/dcim/{cable.html → cable_retrieve.html} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
|
@@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
6
6
|
from django.core.cache import cache
|
|
7
7
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
8
8
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
9
|
-
from django.db import models
|
|
9
|
+
from django.db import models
|
|
10
10
|
from django.db.models import Sum
|
|
11
11
|
from django.utils.functional import classproperty
|
|
12
12
|
|
|
@@ -20,6 +20,7 @@ from nautobot.core.utils.cache import construct_cache_key
|
|
|
20
20
|
from nautobot.core.utils.data import UtilizationData
|
|
21
21
|
from nautobot.dcim.choices import (
|
|
22
22
|
ConsolePortTypeChoices,
|
|
23
|
+
InterfaceDuplexChoices,
|
|
23
24
|
InterfaceModeChoices,
|
|
24
25
|
InterfaceRedundancyGroupProtocolChoices,
|
|
25
26
|
InterfaceStatusChoices,
|
|
@@ -32,6 +33,7 @@ from nautobot.dcim.choices import (
|
|
|
32
33
|
SubdeviceRoleChoices,
|
|
33
34
|
)
|
|
34
35
|
from nautobot.dcim.constants import (
|
|
36
|
+
COPPER_TWISTED_PAIR_IFACE_TYPES,
|
|
35
37
|
NONCONNECTABLE_IFACE_TYPES,
|
|
36
38
|
REARPORT_POSITIONS_MAX,
|
|
37
39
|
REARPORT_POSITIONS_MIN,
|
|
@@ -596,6 +598,86 @@ class BaseInterface(RelationshipModel):
|
|
|
596
598
|
|
|
597
599
|
return super().save(*args, **kwargs)
|
|
598
600
|
|
|
601
|
+
def add_ip_addresses(
|
|
602
|
+
self,
|
|
603
|
+
ip_addresses,
|
|
604
|
+
is_source=False,
|
|
605
|
+
is_destination=False,
|
|
606
|
+
is_default=False,
|
|
607
|
+
is_preferred=False,
|
|
608
|
+
is_primary=False,
|
|
609
|
+
is_secondary=False,
|
|
610
|
+
is_standby=False,
|
|
611
|
+
):
|
|
612
|
+
"""Add one or more IPAddress instances to this interface's `ip_addresses` many-to-many relationship.
|
|
613
|
+
|
|
614
|
+
Args:
|
|
615
|
+
ip_addresses (:obj:`list` or `IPAddress`): Instance of `nautobot.ipam.models.IPAddress` or list of `IPAddress` instances.
|
|
616
|
+
is_source (bool, optional): Is source address. Defaults to False.
|
|
617
|
+
is_destination (bool, optional): Is destination address. Defaults to False.
|
|
618
|
+
is_default (bool, optional): Is default address. Defaults to False.
|
|
619
|
+
is_preferred (bool, optional): Is preferred address. Defaults to False.
|
|
620
|
+
is_primary (bool, optional): Is primary address. Defaults to False.
|
|
621
|
+
is_secondary (bool, optional): Is secondary address. Defaults to False.
|
|
622
|
+
is_standby (bool, optional): Is standby address. Defaults to False.
|
|
623
|
+
|
|
624
|
+
Returns:
|
|
625
|
+
Number of instances added.
|
|
626
|
+
"""
|
|
627
|
+
through_defaults = {
|
|
628
|
+
"is_source": is_source,
|
|
629
|
+
"is_destination": is_destination,
|
|
630
|
+
"is_default": is_default,
|
|
631
|
+
"is_preferred": is_preferred,
|
|
632
|
+
"is_primary": is_primary,
|
|
633
|
+
"is_secondary": is_secondary,
|
|
634
|
+
"is_standby": is_standby,
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if not isinstance(ip_addresses, (tuple, list)):
|
|
638
|
+
ip_addresses = [ip_addresses]
|
|
639
|
+
|
|
640
|
+
# This ensures that ips_to_add only contains IPs which need to be added to the interface. This ensures
|
|
641
|
+
# that len(ips_to_add) accurately represents the results of the action.
|
|
642
|
+
ips_to_add = set(ip_addresses) - set(self.ip_addresses.all())
|
|
643
|
+
|
|
644
|
+
if ips_to_add:
|
|
645
|
+
self.ip_addresses.add(*ips_to_add, through_defaults=through_defaults) # pylint: disable=no-member # Intf/VMIntf both have ip_addresses
|
|
646
|
+
|
|
647
|
+
return len(ips_to_add)
|
|
648
|
+
|
|
649
|
+
add_ip_addresses.alters_data = True
|
|
650
|
+
|
|
651
|
+
def remove_ip_addresses(self, ip_addresses):
|
|
652
|
+
"""Remove one or more IPAddress instances from this interface's `ip_addresses` many-to-many relationship.
|
|
653
|
+
|
|
654
|
+
Args:
|
|
655
|
+
ip_addresses (:obj:`list` or `IPAddress`): Instance of `nautobot.ipam.models.IPAddress` or list of `IPAddress` instances.
|
|
656
|
+
|
|
657
|
+
Returns:
|
|
658
|
+
Number of instances removed.
|
|
659
|
+
"""
|
|
660
|
+
if not isinstance(ip_addresses, (tuple, list)):
|
|
661
|
+
ip_addresses = [ip_addresses]
|
|
662
|
+
|
|
663
|
+
# The delete() call used previously (ref: https://github.com/nautobot/nautobot/issues/3236)
|
|
664
|
+
# meant that if None was passed in, it was silently ignored. Rather that raise an exception,
|
|
665
|
+
# this comprehension maintains backwards compatibility.
|
|
666
|
+
ip_addresses = {ip for ip in ip_addresses if ip is not None}
|
|
667
|
+
|
|
668
|
+
# This checks that the IPs passed in are actually on the interface. By populating
|
|
669
|
+
# ips_to_remove correctly, we ensure that the only IPs passed to remove() are IPs known
|
|
670
|
+
# to be on the interface. This ensures that len(ips_to_remove) accurately represents
|
|
671
|
+
# the results of the action.
|
|
672
|
+
ips_to_remove = ip_addresses & set(self.ip_addresses.all())
|
|
673
|
+
|
|
674
|
+
if ips_to_remove:
|
|
675
|
+
self.ip_addresses.remove(*ips_to_remove) # pylint: disable=no-member # Intf/VMIntf both have ip_addresses
|
|
676
|
+
|
|
677
|
+
return len(ips_to_remove)
|
|
678
|
+
|
|
679
|
+
remove_ip_addresses.alters_data = True
|
|
680
|
+
|
|
599
681
|
|
|
600
682
|
@extras_features(
|
|
601
683
|
"cable_terminations",
|
|
@@ -664,6 +746,9 @@ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInter
|
|
|
664
746
|
blank=True,
|
|
665
747
|
verbose_name="IP Addresses",
|
|
666
748
|
)
|
|
749
|
+
# Operational attributes (distinct from interface type capabilities)
|
|
750
|
+
speed = models.PositiveIntegerField(null=True, blank=True)
|
|
751
|
+
duplex = models.CharField(max_length=10, choices=InterfaceDuplexChoices, blank=True, default="")
|
|
667
752
|
|
|
668
753
|
class Meta(ModularComponentModel.Meta):
|
|
669
754
|
ordering = ("device", "module__id", CollateAsChar("_name")) # Module.ordering is complex; don't order by module
|
|
@@ -798,72 +883,21 @@ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInter
|
|
|
798
883
|
}
|
|
799
884
|
)
|
|
800
885
|
|
|
801
|
-
|
|
802
|
-
self
|
|
803
|
-
ip_addresses,
|
|
804
|
-
is_source=False,
|
|
805
|
-
is_destination=False,
|
|
806
|
-
is_default=False,
|
|
807
|
-
is_preferred=False,
|
|
808
|
-
is_primary=False,
|
|
809
|
-
is_secondary=False,
|
|
810
|
-
is_standby=False,
|
|
811
|
-
):
|
|
812
|
-
"""Add one or more IPAddress instances to this interface's `ip_addresses` many-to-many relationship.
|
|
886
|
+
# Speed/Duplex validation
|
|
887
|
+
self._validate_speed_and_duplex()
|
|
813
888
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
is_source (bool, optional): Is source address. Defaults to False.
|
|
817
|
-
is_destination (bool, optional): Is destination address. Defaults to False.
|
|
818
|
-
is_default (bool, optional): Is default address. Defaults to False.
|
|
819
|
-
is_preferred (bool, optional): Is preferred address. Defaults to False.
|
|
820
|
-
is_primary (bool, optional): Is primary address. Defaults to False.
|
|
821
|
-
is_secondary (bool, optional): Is secondary address. Defaults to False.
|
|
822
|
-
is_standby (bool, optional): Is standby address. Defaults to False.
|
|
889
|
+
def _validate_speed_and_duplex(self):
|
|
890
|
+
"""Validate speed (Kbps) and duplex based on interface type."""
|
|
823
891
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
if not isinstance(ip_addresses, (tuple, list)):
|
|
828
|
-
ip_addresses = [ip_addresses]
|
|
829
|
-
with transaction.atomic():
|
|
830
|
-
for ip in ip_addresses:
|
|
831
|
-
instance = self.ip_addresses.through(
|
|
832
|
-
ip_address=ip,
|
|
833
|
-
interface=self,
|
|
834
|
-
is_source=is_source,
|
|
835
|
-
is_destination=is_destination,
|
|
836
|
-
is_default=is_default,
|
|
837
|
-
is_preferred=is_preferred,
|
|
838
|
-
is_primary=is_primary,
|
|
839
|
-
is_secondary=is_secondary,
|
|
840
|
-
is_standby=is_standby,
|
|
841
|
-
)
|
|
842
|
-
instance.validated_save()
|
|
843
|
-
return len(ip_addresses)
|
|
892
|
+
# Check settings by interface type
|
|
893
|
+
if self.speed and any([self.is_lag, self.is_virtual, self.is_wireless]):
|
|
894
|
+
raise ValidationError({"speed": "Speed is not applicable to this interface type."})
|
|
844
895
|
|
|
845
|
-
|
|
896
|
+
if self.duplex and any([self.is_lag, self.is_virtual, self.is_wireless]):
|
|
897
|
+
raise ValidationError({"duplex": "Duplex is not applicable to this interface type."})
|
|
846
898
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
Args:
|
|
851
|
-
ip_addresses (:obj:`list` or `IPAddress`): Instance of `nautobot.ipam.models.IPAddress` or list of `IPAddress` instances.
|
|
852
|
-
|
|
853
|
-
Returns:
|
|
854
|
-
Number of instances removed.
|
|
855
|
-
"""
|
|
856
|
-
count = 0
|
|
857
|
-
if not isinstance(ip_addresses, (tuple, list)):
|
|
858
|
-
ip_addresses = [ip_addresses]
|
|
859
|
-
with transaction.atomic():
|
|
860
|
-
for ip in ip_addresses:
|
|
861
|
-
qs = self.ip_addresses.through.objects.filter(ip_address=ip, interface=self)
|
|
862
|
-
deleted_count, _ = qs.delete()
|
|
863
|
-
count += deleted_count
|
|
864
|
-
return count
|
|
865
|
-
|
|
866
|
-
remove_ip_addresses.alters_data = True
|
|
899
|
+
if self.duplex and self.type not in COPPER_TWISTED_PAIR_IFACE_TYPES:
|
|
900
|
+
raise ValidationError({"duplex": "Duplex is only applicable to copper twisted-pair interfaces."})
|
|
867
901
|
|
|
868
902
|
@property
|
|
869
903
|
def is_connectable(self):
|
|
@@ -997,7 +1031,7 @@ class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
|
|
|
997
1031
|
on_delete=models.CASCADE,
|
|
998
1032
|
related_name="interface_redundancy_group_associations",
|
|
999
1033
|
)
|
|
1000
|
-
priority = models.
|
|
1034
|
+
priority = models.PositiveIntegerField()
|
|
1001
1035
|
is_metadata_associable_model = False
|
|
1002
1036
|
|
|
1003
1037
|
class Meta:
|
nautobot/dcim/models/devices.py
CHANGED
|
@@ -25,6 +25,7 @@ from nautobot.dcim.choices import (
|
|
|
25
25
|
ControllerCapabilitiesChoices,
|
|
26
26
|
DeviceFaceChoices,
|
|
27
27
|
DeviceRedundancyGroupFailoverStrategyChoices,
|
|
28
|
+
DeviceUniquenessChoices,
|
|
28
29
|
SoftwareImageFileHashingAlgorithmChoices,
|
|
29
30
|
SubdeviceRoleChoices,
|
|
30
31
|
)
|
|
@@ -391,6 +392,10 @@ class DeviceType(PrimaryModel):
|
|
|
391
392
|
def display(self):
|
|
392
393
|
return f"{self.manufacturer.name} {self.model}"
|
|
393
394
|
|
|
395
|
+
@property
|
|
396
|
+
def page_title(self):
|
|
397
|
+
return str(self)
|
|
398
|
+
|
|
394
399
|
@property
|
|
395
400
|
def is_parent_device(self):
|
|
396
401
|
return self.subdevice_role == SubdeviceRoleChoices.ROLE_PARENT
|
|
@@ -620,7 +625,7 @@ class Device(PrimaryModel, ConfigContextModel):
|
|
|
620
625
|
null=True,
|
|
621
626
|
verbose_name="Device Redundancy Group",
|
|
622
627
|
)
|
|
623
|
-
device_redundancy_group_priority = models.
|
|
628
|
+
device_redundancy_group_priority = models.PositiveIntegerField(
|
|
624
629
|
blank=True,
|
|
625
630
|
null=True,
|
|
626
631
|
validators=[MinValueValidator(1)],
|
|
@@ -682,19 +687,20 @@ class Device(PrimaryModel, ConfigContextModel):
|
|
|
682
687
|
@classproperty # https://github.com/PyCQA/pylint-django/issues/240
|
|
683
688
|
def natural_key_field_names(cls): # pylint: disable=no-self-argument
|
|
684
689
|
"""
|
|
685
|
-
|
|
690
|
+
Check DEVICE_UNIQUENESS from settings or Constance and return proper field.
|
|
686
691
|
"""
|
|
687
|
-
if get_settings_or_config("
|
|
688
|
-
# opt-in
|
|
692
|
+
if get_settings_or_config("DEVICE_UNIQUENESS") == DeviceUniquenessChoices.NAME:
|
|
693
|
+
# Simplified pseudo-natural key (opt-in for name-only uniqueness)
|
|
689
694
|
return ["name"]
|
|
695
|
+
elif get_settings_or_config("DEVICE_UNIQUENESS") == DeviceUniquenessChoices.LOCATION_TENANT_NAME:
|
|
696
|
+
# Full natural key based on tenant, location, and name
|
|
697
|
+
return ["name", "tenant", "location"]
|
|
690
698
|
else:
|
|
691
|
-
|
|
692
|
-
return ["name", "tenant", "location"] # location should be last since it's potentially variadic
|
|
699
|
+
return ["pk"]
|
|
693
700
|
|
|
694
701
|
class Meta:
|
|
695
702
|
ordering = ("_name",) # Name may be null
|
|
696
703
|
unique_together = (
|
|
697
|
-
("location", "tenant", "name"), # See validate_unique below
|
|
698
704
|
("rack", "position", "face"),
|
|
699
705
|
("virtual_chassis", "vc_position"),
|
|
700
706
|
)
|
|
@@ -716,16 +722,6 @@ class Device(PrimaryModel, ConfigContextModel):
|
|
|
716
722
|
else:
|
|
717
723
|
self.clusters.set([cluster])
|
|
718
724
|
|
|
719
|
-
def validate_unique(self, exclude=None):
|
|
720
|
-
# Check for a duplicate name on a device assigned to the same Location and no Tenant. This is necessary
|
|
721
|
-
# because Django does not consider two NULL fields to be equal, and thus will not trigger a violation
|
|
722
|
-
# of the uniqueness constraint without manual intervention.
|
|
723
|
-
if self.name and hasattr(self, "location") and self.tenant is None:
|
|
724
|
-
if Device.objects.exclude(pk=self.pk).filter(name=self.name, location=self.location, tenant__isnull=True):
|
|
725
|
-
raise ValidationError({"name": "A device with this name already exists."})
|
|
726
|
-
|
|
727
|
-
super().validate_unique(exclude)
|
|
728
|
-
|
|
729
725
|
def clean(self):
|
|
730
726
|
from nautobot.ipam import models as ipam_models # circular import workaround
|
|
731
727
|
|
|
@@ -909,6 +905,11 @@ class Device(PrimaryModel, ConfigContextModel):
|
|
|
909
905
|
def save(self, *args, **kwargs):
|
|
910
906
|
is_new = not self.present_in_database
|
|
911
907
|
|
|
908
|
+
# to avoid circular import
|
|
909
|
+
from nautobot.dcim.custom_validators import DeviceUniquenessValidator
|
|
910
|
+
|
|
911
|
+
DeviceUniquenessValidator(self).clean()
|
|
912
|
+
|
|
912
913
|
super().save(*args, **kwargs)
|
|
913
914
|
|
|
914
915
|
# Apply any pending cluster assignment that was deferred during creation
|
|
@@ -1953,10 +1954,19 @@ class Module(PrimaryModel):
|
|
|
1953
1954
|
def display(self):
|
|
1954
1955
|
if self.location:
|
|
1955
1956
|
return f"{self!s} at location {self.location}"
|
|
1956
|
-
|
|
1957
|
+
if self.parent_module_bay.parent_device is not None:
|
|
1957
1958
|
return f"{self.module_type!s} installed in {self.parent_module_bay.parent_device.display}"
|
|
1958
|
-
|
|
1959
|
-
|
|
1959
|
+
|
|
1960
|
+
return f"{self.module_type!s} installed in {self.parent_module_bay.parent_module.display}"
|
|
1961
|
+
|
|
1962
|
+
@property
|
|
1963
|
+
def page_title(self):
|
|
1964
|
+
if self.location:
|
|
1965
|
+
return f"{self.module_type!s} at location {self.location}"
|
|
1966
|
+
if self.parent_module_bay.parent_device is not None:
|
|
1967
|
+
return f"{self.module_type!s} installed in {self.parent_module_bay.parent_device.display}"
|
|
1968
|
+
|
|
1969
|
+
return f"{self.module_type!s} installed in {self.parent_module_bay.parent_module.module_type!s}"
|
|
1960
1970
|
|
|
1961
1971
|
@property
|
|
1962
1972
|
def device(self):
|
nautobot/dcim/navigation.py
CHANGED
|
@@ -4,12 +4,13 @@ from nautobot.core.apps import (
|
|
|
4
4
|
NavMenuItem,
|
|
5
5
|
NavMenuTab,
|
|
6
6
|
)
|
|
7
|
+
from nautobot.core.ui.choices import NavigationIconChoices, NavigationWeightChoices
|
|
7
8
|
|
|
8
9
|
menu_items = (
|
|
9
10
|
NavMenuTab(
|
|
10
11
|
name="Organization",
|
|
11
|
-
icon=
|
|
12
|
-
weight=
|
|
12
|
+
icon=NavigationIconChoices.ORGANIZATION,
|
|
13
|
+
weight=NavigationWeightChoices.ORGANIZATION,
|
|
13
14
|
groups=(
|
|
14
15
|
NavMenuGroup(
|
|
15
16
|
name="Locations",
|
|
@@ -116,8 +117,8 @@ menu_items = (
|
|
|
116
117
|
),
|
|
117
118
|
NavMenuTab(
|
|
118
119
|
name="Devices",
|
|
119
|
-
icon=
|
|
120
|
-
weight=
|
|
120
|
+
icon=NavigationIconChoices.DEVICES,
|
|
121
|
+
weight=NavigationWeightChoices.DEVICES,
|
|
121
122
|
groups=(
|
|
122
123
|
NavMenuGroup(
|
|
123
124
|
name="Devices",
|
|
@@ -549,8 +550,8 @@ menu_items = (
|
|
|
549
550
|
),
|
|
550
551
|
NavMenuTab(
|
|
551
552
|
name="Power",
|
|
552
|
-
icon=
|
|
553
|
-
weight=
|
|
553
|
+
icon=NavigationIconChoices.POWER,
|
|
554
|
+
weight=NavigationWeightChoices.POWER,
|
|
554
555
|
groups=(
|
|
555
556
|
NavMenuGroup(
|
|
556
557
|
name="Power",
|
nautobot/dcim/tables/devices.py
CHANGED
|
@@ -11,6 +11,7 @@ from nautobot.core.tables import (
|
|
|
11
11
|
TagColumn,
|
|
12
12
|
ToggleColumn,
|
|
13
13
|
)
|
|
14
|
+
from nautobot.core.templatetags.helpers import humanize_speed
|
|
14
15
|
from nautobot.dcim.models import (
|
|
15
16
|
ConsolePort,
|
|
16
17
|
ConsoleServerPort,
|
|
@@ -295,6 +296,7 @@ class DeviceImportTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
295
296
|
|
|
296
297
|
class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
297
298
|
pk = ToggleColumn()
|
|
299
|
+
id = tables.Column(linkify=True, verbose_name="ID")
|
|
298
300
|
module_type = tables.Column(
|
|
299
301
|
linkify=lambda record: record.module_type.get_absolute_url(),
|
|
300
302
|
verbose_name="Type",
|
|
@@ -315,6 +317,7 @@ class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
315
317
|
model = Module
|
|
316
318
|
fields = (
|
|
317
319
|
"pk",
|
|
320
|
+
"id",
|
|
318
321
|
"module_type",
|
|
319
322
|
"module_family",
|
|
320
323
|
"parent_module_bay",
|
|
@@ -329,6 +332,7 @@ class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
329
332
|
)
|
|
330
333
|
default_columns = (
|
|
331
334
|
"pk",
|
|
335
|
+
"id",
|
|
332
336
|
"module_type",
|
|
333
337
|
"module_family",
|
|
334
338
|
"parent_module_bay",
|
|
@@ -725,6 +729,8 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
|
|
|
725
729
|
url_params={"interfaces": "pk"},
|
|
726
730
|
verbose_name="Virtual Device Contexts",
|
|
727
731
|
)
|
|
732
|
+
speed = tables.Column(verbose_name="Speed", accessor="speed", orderable=True)
|
|
733
|
+
duplex = tables.Column(verbose_name="Duplex", accessor="duplex", orderable=True)
|
|
728
734
|
|
|
729
735
|
class Meta(ModularDeviceComponentTable.Meta):
|
|
730
736
|
model = Interface
|
|
@@ -738,6 +744,8 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
|
|
|
738
744
|
"label",
|
|
739
745
|
"enabled",
|
|
740
746
|
"type",
|
|
747
|
+
"speed",
|
|
748
|
+
"duplex",
|
|
741
749
|
"mgmt_only",
|
|
742
750
|
"mtu",
|
|
743
751
|
"vrf",
|
|
@@ -763,9 +771,13 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
|
|
|
763
771
|
"label",
|
|
764
772
|
"enabled",
|
|
765
773
|
"type",
|
|
774
|
+
"speed",
|
|
766
775
|
"description",
|
|
767
776
|
)
|
|
768
777
|
|
|
778
|
+
def render_speed(self, record):
|
|
779
|
+
return humanize_speed(record.speed)
|
|
780
|
+
|
|
769
781
|
|
|
770
782
|
class DeviceModuleInterfaceTable(InterfaceTable):
|
|
771
783
|
name = tables.TemplateColumn(
|
|
@@ -791,6 +803,8 @@ class DeviceModuleInterfaceTable(InterfaceTable):
|
|
|
791
803
|
"module",
|
|
792
804
|
"enabled",
|
|
793
805
|
"type",
|
|
806
|
+
"speed",
|
|
807
|
+
"duplex",
|
|
794
808
|
"parent_interface",
|
|
795
809
|
"bridge",
|
|
796
810
|
"lag",
|
|
@@ -836,6 +850,9 @@ class DeviceModuleInterfaceTable(InterfaceTable):
|
|
|
836
850
|
"data-name": lambda record: record.name,
|
|
837
851
|
}
|
|
838
852
|
|
|
853
|
+
def render_speed(self, record):
|
|
854
|
+
return humanize_speed(record.speed)
|
|
855
|
+
|
|
839
856
|
|
|
840
857
|
class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable):
|
|
841
858
|
rear_port_position = tables.Column(verbose_name="Position")
|
|
@@ -1288,6 +1305,7 @@ class InterfaceRedundancyGroupAssociationTable(BaseTable):
|
|
|
1288
1305
|
"""Table for list view."""
|
|
1289
1306
|
|
|
1290
1307
|
pk = ToggleColumn()
|
|
1308
|
+
interface__enabled = BooleanColumn()
|
|
1291
1309
|
interface_redundancy_group = tables.Column(linkify=True, verbose_name="Group Name")
|
|
1292
1310
|
interface_redundancy_group__virtual_ip = tables.Column(linkify=True, verbose_name="Virtual IP")
|
|
1293
1311
|
interface_redundancy_group__protocol_group_id = tables.Column(verbose_name="Group ID")
|
|
@@ -8,6 +8,7 @@ from nautobot.core.tables import (
|
|
|
8
8
|
TagColumn,
|
|
9
9
|
ToggleColumn,
|
|
10
10
|
)
|
|
11
|
+
from nautobot.core.templatetags.helpers import humanize_speed
|
|
11
12
|
from nautobot.dcim.models import (
|
|
12
13
|
ConsolePortTemplate,
|
|
13
14
|
ConsoleServerPortTemplate,
|
|
@@ -270,6 +271,8 @@ class PowerOutletTemplateTable(ComponentTemplateTable):
|
|
|
270
271
|
|
|
271
272
|
class InterfaceTemplateTable(ComponentTemplateTable):
|
|
272
273
|
mgmt_only = BooleanColumn(verbose_name="Management Only")
|
|
274
|
+
speed = tables.Column(verbose_name="Speed", accessor="speed", orderable=True)
|
|
275
|
+
duplex = tables.Column(verbose_name="Duplex", accessor="duplex", orderable=True)
|
|
273
276
|
actions = ButtonsColumn(
|
|
274
277
|
model=InterfaceTemplate,
|
|
275
278
|
buttons=("edit", "delete"),
|
|
@@ -278,9 +281,13 @@ class InterfaceTemplateTable(ComponentTemplateTable):
|
|
|
278
281
|
|
|
279
282
|
class Meta(BaseTable.Meta):
|
|
280
283
|
model = InterfaceTemplate
|
|
281
|
-
fields = ("pk", "name", "label", "mgmt_only", "type", "description", "actions")
|
|
284
|
+
fields = ("pk", "name", "label", "mgmt_only", "type", "speed", "duplex", "description", "actions")
|
|
285
|
+
default_columns = ("pk", "name", "label", "mgmt_only", "type", "speed", "description", "actions")
|
|
282
286
|
empty_text = "None"
|
|
283
287
|
|
|
288
|
+
def render_speed(self, record):
|
|
289
|
+
return humanize_speed(record.speed)
|
|
290
|
+
|
|
284
291
|
|
|
285
292
|
class FrontPortTemplateTable(ComponentTemplateTable):
|
|
286
293
|
rear_port_position = tables.Column(verbose_name="Position")
|
nautobot/dcim/tables/racks.py
CHANGED
|
@@ -102,7 +102,7 @@ TREE_LINK = """
|
|
|
102
102
|
|
|
103
103
|
POWERFEED_CABLE = """
|
|
104
104
|
<a href="{{ value.get_absolute_url }}">{{ value }}</a>
|
|
105
|
-
<a href="{% url 'dcim:powerfeed_trace' pk=record.pk %}" class="btn btn-primary btn-
|
|
105
|
+
<a href="{% url 'dcim:powerfeed_trace' pk=record.pk %}" class="btn btn-primary btn-sm" title="Trace">
|
|
106
106
|
<i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
|
|
107
107
|
</a>
|
|
108
108
|
"""
|
|
@@ -137,8 +137,8 @@ CONSOLEPORT_BUTTONS = """
|
|
|
137
137
|
<li><a href="{% url 'dcim:consoleport_trace' pk=record.pk %}" class="dropdown-item text-primary"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
138
138
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
|
139
139
|
{% elif perms.dcim.add_cable %}
|
|
140
|
-
<li><a
|
|
141
|
-
<li><a
|
|
140
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
141
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-lan-connect" aria-hidden="true"></span>Mark installed</a></li>
|
|
142
142
|
<li>
|
|
143
143
|
<a href="{% url 'dcim:consoleport_connect' termination_a_id=record.pk termination_b_type='console-server-port' %}?return_url={{ request.path }}" class="dropdown-item text-success">
|
|
144
144
|
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
|
|
@@ -165,8 +165,8 @@ CONSOLESERVERPORT_BUTTONS = """
|
|
|
165
165
|
<li><a href="{% url 'dcim:consoleserverport_trace' pk=record.pk %}" class="dropdown-item text-primary"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
166
166
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
|
167
167
|
{% elif perms.dcim.add_cable %}
|
|
168
|
-
<li><a
|
|
169
|
-
<li><a
|
|
168
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
169
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-lan-connect" aria-hidden="true"></span>Mark installed</a></li>
|
|
170
170
|
<li>
|
|
171
171
|
<a href="{% url 'dcim:consoleserverport_connect' termination_a_id=record.pk termination_b_type='console-port' %}?return_url={{ request.path }}" class="dropdown-item text-success">
|
|
172
172
|
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
|
|
@@ -193,8 +193,8 @@ POWERPORT_BUTTONS = """
|
|
|
193
193
|
<li><a href="{% url 'dcim:powerport_trace' pk=record.pk %}" class="dropdown-item text-primary"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
194
194
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
|
195
195
|
{% elif perms.dcim.add_cable %}
|
|
196
|
-
<li><a
|
|
197
|
-
<li><a
|
|
196
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
197
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-lan-connect" aria-hidden="true"></span>Mark installed</a></li>
|
|
198
198
|
<li>
|
|
199
199
|
<a href="{% url 'dcim:powerport_connect' termination_a_id=record.pk termination_b_type='power-outlet' %}?return_url={{ request.path }}" class="dropdown-item text-success">
|
|
200
200
|
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
|
|
@@ -215,8 +215,8 @@ POWEROUTLET_BUTTONS = """
|
|
|
215
215
|
<li><a href="{% url 'dcim:poweroutlet_trace' pk=record.pk %}" class="dropdown-item text-primary"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
216
216
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
|
217
217
|
{% elif perms.dcim.add_cable %}
|
|
218
|
-
<li><a
|
|
219
|
-
<li><a
|
|
218
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
219
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-lan-connect" aria-hidden="true"></span>Mark installed</a></li>
|
|
220
220
|
<li>
|
|
221
221
|
<a href="{% url 'dcim:poweroutlet_connect' termination_a_id=record.pk termination_b_type='power-port' %}?return_url={{ request.path }}" class="dropdown-item text-success">
|
|
222
222
|
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
|
|
@@ -239,8 +239,8 @@ INTERFACE_BUTTONS = """
|
|
|
239
239
|
<li><a href="{% url 'dcim:interface_trace' pk=record.pk %}" class="dropdown-item text-primary"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span><Trace/a></li>
|
|
240
240
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
|
241
241
|
{% elif record.is_connectable and perms.dcim.add_cable %}
|
|
242
|
-
<li><a
|
|
243
|
-
<li><a
|
|
242
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
243
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-lan-connect" aria-hidden="true"></span>Mark installed</a></li>
|
|
244
244
|
<li>
|
|
245
245
|
<a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={{ request.path }}" class="dropdown-item text-success">
|
|
246
246
|
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
|
|
@@ -273,8 +273,8 @@ FRONTPORT_BUTTONS = """
|
|
|
273
273
|
<li><a href="{% url 'dcim:frontport_trace' pk=record.pk %}" class="dropdown-item text-primary"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
274
274
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
|
275
275
|
{% elif perms.dcim.add_cable %}
|
|
276
|
-
<li><a
|
|
277
|
-
<li><a
|
|
276
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
277
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-lan-connect" aria-hidden="true"></span>Mark installed</a></li>
|
|
278
278
|
<li>
|
|
279
279
|
<a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={{ request.path }}" class="dropdown-item text-success">
|
|
280
280
|
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
|
|
@@ -319,8 +319,8 @@ REARPORT_BUTTONS = """
|
|
|
319
319
|
<li><a href="{% url 'dcim:rearport_trace' pk=record.pk %}" class="dropdown-item text-primary"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
320
320
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
|
321
321
|
{% elif perms.dcim.add_cable %}
|
|
322
|
-
<li><a
|
|
323
|
-
<li><a
|
|
322
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-transit-connection-variant" aria-hidden="true"></span>Trace</a></li>
|
|
323
|
+
<li><a class="dropdown-item disabled" aria-disabled="true"><span class="mdi mdi-lan-connect" aria-hidden="true"></span>Mark installed</a></li>
|
|
324
324
|
<li>
|
|
325
325
|
<a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={{ request.path }}" class="dropdown-item text-success">
|
|
326
326
|
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
|