nautobot 3.0.0a2__py3-none-any.whl → 3.0.0a3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/choices.py +0 -2
- nautobot/apps/filters.py +7 -9
- nautobot/apps/models.py +2 -2
- nautobot/apps/ui.py +9 -1
- nautobot/circuits/filters.py +3 -2
- nautobot/circuits/navigation.py +3 -2
- nautobot/circuits/templates/circuits/circuit.html +1 -1
- nautobot/circuits/templates/circuits/circuit_create.html +3 -3
- nautobot/circuits/templates/circuits/circuittermination.html +1 -1
- nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
- nautobot/circuits/templates/circuits/circuittype.html +1 -1
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
- nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
- nautobot/circuits/templates/circuits/providernetwork.html +1 -1
- nautobot/circuits/tests/integration/test_circuit.py +10 -13
- 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 +1 -0
- nautobot/core/api/views.py +8 -0
- nautobot/core/apps/__init__.py +11 -10
- nautobot/core/celery/__init__.py +3 -5
- nautobot/core/checks.py +46 -0
- nautobot/core/cli/bootstrap_v3_to_v5.py +70 -1
- nautobot/core/cli/migrate_deprecated_templates.py +200 -0
- nautobot/core/constants.py +3 -0
- nautobot/core/context_processors.py +9 -1
- nautobot/core/forms/forms.py +1 -1
- nautobot/core/jobs/__init__.py +6 -3
- nautobot/core/jobs/groups.py +31 -1
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/tree_queries.py +10 -5
- nautobot/core/settings.py +18 -12
- nautobot/core/settings.yaml +13 -7
- 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/config/config.html +12 -12
- nautobot/core/templates/admin/index.html +3 -3
- 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/panel.html +3 -3
- nautobot/core/templates/components/tab/content_wrapper.html +2 -3
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
- nautobot/core/templates/echarts/echarts.html +1 -1
- nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
- nautobot/core/templates/generic/object_bulk_create.html +4 -3
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
- 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_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 +2 -3
- nautobot/core/templates/graphene/graphiql.html +7 -7
- 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/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +1 -1
- nautobot/core/templates/inc/image_attachments.html +2 -1
- nautobot/core/templates/inc/nav_menu.html +2 -1
- nautobot/core/templates/inc/search_panel.html +4 -4
- nautobot/core/templates/login.html +4 -2
- nautobot/core/templates/nautobot_config.py.j2 +6 -5
- nautobot/core/templates/redoc_ui.html +7 -0
- 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 +4 -4
- nautobot/core/templates/utilities/theme_preview.html +16 -3
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templatetags/helpers.py +52 -6
- nautobot/core/testing/api.py +68 -9
- nautobot/core/testing/filters.py +0 -23
- nautobot/core/testing/integration.py +23 -10
- nautobot/core/testing/mixins.py +2 -0
- nautobot/core/testing/views.py +4 -0
- nautobot/core/tests/integration/test_app_home.py +34 -30
- nautobot/core/tests/integration/test_app_navbar.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +9 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_breadcrumbs.py +6 -7
- 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 +75 -1
- 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_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +53 -13
- nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
- nautobot/core/tests/test_tree_queries.py +14 -1
- nautobot/core/tests/test_ui.py +1 -1
- nautobot/core/tests/test_utils.py +31 -4
- nautobot/core/tests/test_views.py +159 -31
- nautobot/core/ui/breadcrumbs.py +2 -12
- nautobot/core/ui/choices.py +142 -10
- nautobot/core/ui/constants.py +76 -12
- nautobot/core/ui/object_detail.py +92 -12
- nautobot/core/urls.py +12 -1
- nautobot/core/utils/cache.py +2 -1
- nautobot/core/utils/filtering.py +17 -17
- nautobot/core/utils/lookup.py +3 -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 +15 -3
- nautobot/core/views/renderers.py +2 -0
- 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 +0 -12
- 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 +0 -13
- 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} +2 -35
- nautobot/dcim/forms.py +1 -1
- 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/device_components.py +81 -68
- nautobot/dcim/models/devices.py +13 -16
- nautobot/dcim/navigation.py +7 -6
- nautobot/dcim/tables/devices.py +3 -0
- nautobot/dcim/tables/template_code.py +14 -14
- nautobot/dcim/templates/dcim/cable.html +2 -61
- nautobot/dcim/templates/dcim/cable_connect.html +28 -112
- nautobot/dcim/templates/dcim/cable_edit.html +2 -5
- nautobot/dcim/templates/dcim/cable_retrieve.html +61 -0
- nautobot/dcim/templates/dcim/cable_trace.html +1 -3
- nautobot/dcim/templates/dcim/cable_update.html +5 -0
- nautobot/dcim/templates/dcim/consoleport.html +6 -5
- nautobot/dcim/templates/dcim/consoleserverport.html +6 -5
- nautobot/dcim/templates/dcim/device/config.html +2 -2
- nautobot/dcim/templates/dcim/device/consoleports.html +1 -1
- nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -1
- nautobot/dcim/templates/dcim/device/devicebays.html +1 -1
- nautobot/dcim/templates/dcim/device/frontports.html +1 -1
- nautobot/dcim/templates/dcim/device/interfaces.html +1 -1
- nautobot/dcim/templates/dcim/device/inventory.html +1 -1
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
- nautobot/dcim/templates/dcim/device/modulebays.html +1 -1
- nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -1
- nautobot/dcim/templates/dcim/device/powerports.html +1 -1
- nautobot/dcim/templates/dcim/device/rearports.html +1 -1
- nautobot/dcim/templates/dcim/device/status.html +8 -8
- nautobot/dcim/templates/dcim/device/wireless.html +1 -1
- 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_populate.html +2 -2
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- 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 +9 -8
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/interface.html +26 -6
- nautobot/dcim/templates/dcim/interface_bulk_delete.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/location_retrieve.html +1 -242
- nautobot/dcim/templates/dcim/module/base.html +49 -9
- nautobot/dcim/templates/dcim/module_list.html +57 -8
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
- nautobot/dcim/templates/dcim/platform_create.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +5 -4
- nautobot/dcim/templates/dcim/rack_elevation_list.html +16 -4
- nautobot/dcim/templates/dcim/rack_retrieve.html +33 -15
- nautobot/dcim/templates/dcim/rearport.html +7 -6
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
- nautobot/dcim/templates/dcim/virtualchassis_update.html +14 -6
- nautobot/dcim/tests/integration/test_controller.py +1 -0
- nautobot/dcim/tests/test_api.py +8 -0
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +12 -6
- nautobot/dcim/tests/test_models.py +63 -4
- nautobot/dcim/tests/test_views.py +63 -22
- nautobot/dcim/urls.py +64 -21
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +547 -273
- nautobot/extras/api/views.py +9 -1
- nautobot/extras/choices.py +2 -13
- 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} +14 -46
- nautobot/extras/forms/forms.py +5 -13
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/management/__init__.py +9 -0
- 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/models/__init__.py +1 -2
- nautobot/extras/models/approvals.py +22 -13
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/groups.py +44 -5
- nautobot/extras/models/jobs.py +59 -1
- nautobot/extras/models/mixins.py +28 -0
- nautobot/extras/models/models.py +13 -0
- 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/tables.py +3 -3
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/views.py +0 -4
- nautobot/extras/signals.py +20 -19
- nautobot/extras/tables.py +52 -68
- 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/computedfield.html +1 -1
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/customfield.html +1 -1
- 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/graphqlquery_retrieve.html +1 -96
- 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 -37
- nautobot/extras/templates/extras/plugin_detail.html +26 -21
- 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/secretsgroup.html +1 -1
- nautobot/extras/templates/extras/tag.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_configcontextschema.py +27 -26
- nautobot/extras/tests/integration/test_customfields.py +8 -7
- nautobot/extras/tests/integration/test_dynamicgroups.py +5 -1
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +18 -6
- nautobot/extras/tests/test_api.py +27 -18
- nautobot/extras/tests/test_approvals.py +38 -38
- nautobot/extras/tests/test_changelog.py +35 -3
- 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 +21 -19
- 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 +13 -31
- nautobot/extras/tests/test_plugins.py +36 -10
- nautobot/extras/tests/test_views.py +31 -30
- nautobot/extras/views.py +81 -19
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +27 -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 +13 -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_update.html +15 -0
- nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
- nautobot/ipam/templates/ipam/prefix_list.html +14 -13
- nautobot/ipam/templates/ipam/service.html +1 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- 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 +10 -0
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +43 -1
- 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 +46 -6
- nautobot/project-static/dist/css/nautobot.css +1 -1
- nautobot/project-static/dist/css/nautobot.css.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/interface_filtering.js +20 -16
- nautobot/project-static/nautobot-icons/battery-3.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/src/js/collapse.js +3 -3
- nautobot/ui/src/js/nautobot.js +16 -0
- nautobot/ui/src/scss/colors.scss +1 -1
- nautobot/ui/src/scss/nautobot.scss +61 -28
- 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/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.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_models.py +44 -4
- 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/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.0a3.dist-info}/METADATA +14 -14
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +417 -366
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
- nautobot/data_validation/template_content.py +0 -42
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.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
|
|
|
@@ -596,6 +596,86 @@ class BaseInterface(RelationshipModel):
|
|
|
596
596
|
|
|
597
597
|
return super().save(*args, **kwargs)
|
|
598
598
|
|
|
599
|
+
def add_ip_addresses(
|
|
600
|
+
self,
|
|
601
|
+
ip_addresses,
|
|
602
|
+
is_source=False,
|
|
603
|
+
is_destination=False,
|
|
604
|
+
is_default=False,
|
|
605
|
+
is_preferred=False,
|
|
606
|
+
is_primary=False,
|
|
607
|
+
is_secondary=False,
|
|
608
|
+
is_standby=False,
|
|
609
|
+
):
|
|
610
|
+
"""Add one or more IPAddress instances to this interface's `ip_addresses` many-to-many relationship.
|
|
611
|
+
|
|
612
|
+
Args:
|
|
613
|
+
ip_addresses (:obj:`list` or `IPAddress`): Instance of `nautobot.ipam.models.IPAddress` or list of `IPAddress` instances.
|
|
614
|
+
is_source (bool, optional): Is source address. Defaults to False.
|
|
615
|
+
is_destination (bool, optional): Is destination address. Defaults to False.
|
|
616
|
+
is_default (bool, optional): Is default address. Defaults to False.
|
|
617
|
+
is_preferred (bool, optional): Is preferred address. Defaults to False.
|
|
618
|
+
is_primary (bool, optional): Is primary address. Defaults to False.
|
|
619
|
+
is_secondary (bool, optional): Is secondary address. Defaults to False.
|
|
620
|
+
is_standby (bool, optional): Is standby address. Defaults to False.
|
|
621
|
+
|
|
622
|
+
Returns:
|
|
623
|
+
Number of instances added.
|
|
624
|
+
"""
|
|
625
|
+
through_defaults = {
|
|
626
|
+
"is_source": is_source,
|
|
627
|
+
"is_destination": is_destination,
|
|
628
|
+
"is_default": is_default,
|
|
629
|
+
"is_preferred": is_preferred,
|
|
630
|
+
"is_primary": is_primary,
|
|
631
|
+
"is_secondary": is_secondary,
|
|
632
|
+
"is_standby": is_standby,
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if not isinstance(ip_addresses, (tuple, list)):
|
|
636
|
+
ip_addresses = [ip_addresses]
|
|
637
|
+
|
|
638
|
+
# This ensures that ips_to_add only contains IPs which need to be added to the interface. This ensures
|
|
639
|
+
# that len(ips_to_add) accurately represents the results of the action.
|
|
640
|
+
ips_to_add = set(ip_addresses) - set(self.ip_addresses.all())
|
|
641
|
+
|
|
642
|
+
if ips_to_add:
|
|
643
|
+
self.ip_addresses.add(*ips_to_add, through_defaults=through_defaults) # pylint: disable=no-member # Intf/VMIntf both have ip_addresses
|
|
644
|
+
|
|
645
|
+
return len(ips_to_add)
|
|
646
|
+
|
|
647
|
+
add_ip_addresses.alters_data = True
|
|
648
|
+
|
|
649
|
+
def remove_ip_addresses(self, ip_addresses):
|
|
650
|
+
"""Remove one or more IPAddress instances from this interface's `ip_addresses` many-to-many relationship.
|
|
651
|
+
|
|
652
|
+
Args:
|
|
653
|
+
ip_addresses (:obj:`list` or `IPAddress`): Instance of `nautobot.ipam.models.IPAddress` or list of `IPAddress` instances.
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
Number of instances removed.
|
|
657
|
+
"""
|
|
658
|
+
if not isinstance(ip_addresses, (tuple, list)):
|
|
659
|
+
ip_addresses = [ip_addresses]
|
|
660
|
+
|
|
661
|
+
# The delete() call used previously (ref: https://github.com/nautobot/nautobot/issues/3236)
|
|
662
|
+
# meant that if None was passed in, it was silently ignored. Rather that raise an exception,
|
|
663
|
+
# this comprehension maintains backwards compatibility.
|
|
664
|
+
ip_addresses = {ip for ip in ip_addresses if ip is not None}
|
|
665
|
+
|
|
666
|
+
# This checks that the IPs passed in are actually on the interface. By populating
|
|
667
|
+
# ips_to_remove correctly, we ensure that the only IPs passed to remove() are IPs known
|
|
668
|
+
# to be on the interface. This ensures that len(ips_to_remove) accurately represents
|
|
669
|
+
# the results of the action.
|
|
670
|
+
ips_to_remove = ip_addresses & set(self.ip_addresses.all())
|
|
671
|
+
|
|
672
|
+
if ips_to_remove:
|
|
673
|
+
self.ip_addresses.remove(*ips_to_remove) # pylint: disable=no-member # Intf/VMIntf both have ip_addresses
|
|
674
|
+
|
|
675
|
+
return len(ips_to_remove)
|
|
676
|
+
|
|
677
|
+
remove_ip_addresses.alters_data = True
|
|
678
|
+
|
|
599
679
|
|
|
600
680
|
@extras_features(
|
|
601
681
|
"cable_terminations",
|
|
@@ -798,73 +878,6 @@ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInter
|
|
|
798
878
|
}
|
|
799
879
|
)
|
|
800
880
|
|
|
801
|
-
def add_ip_addresses(
|
|
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.
|
|
813
|
-
|
|
814
|
-
Args:
|
|
815
|
-
ip_addresses (:obj:`list` or `IPAddress`): Instance of `nautobot.ipam.models.IPAddress` or list of `IPAddress` instances.
|
|
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.
|
|
823
|
-
|
|
824
|
-
Returns:
|
|
825
|
-
Number of instances added.
|
|
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)
|
|
844
|
-
|
|
845
|
-
add_ip_addresses.alters_data = True
|
|
846
|
-
|
|
847
|
-
def remove_ip_addresses(self, ip_addresses):
|
|
848
|
-
"""Remove one or more IPAddress instances from this interface's `ip_addresses` many-to-many relationship.
|
|
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
|
|
867
|
-
|
|
868
881
|
@property
|
|
869
882
|
def is_connectable(self):
|
|
870
883
|
return self.type not in NONCONNECTABLE_IFACE_TYPES
|
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
|
)
|
|
@@ -682,19 +683,20 @@ class Device(PrimaryModel, ConfigContextModel):
|
|
|
682
683
|
@classproperty # https://github.com/PyCQA/pylint-django/issues/240
|
|
683
684
|
def natural_key_field_names(cls): # pylint: disable=no-self-argument
|
|
684
685
|
"""
|
|
685
|
-
|
|
686
|
+
Check DEVICE_UNIQUENESS from settings or Constance and return proper field.
|
|
686
687
|
"""
|
|
687
|
-
if get_settings_or_config("
|
|
688
|
-
# opt-in
|
|
688
|
+
if get_settings_or_config("DEVICE_UNIQUENESS") == DeviceUniquenessChoices.NAME:
|
|
689
|
+
# Simplified pseudo-natural key (opt-in for name-only uniqueness)
|
|
689
690
|
return ["name"]
|
|
691
|
+
elif get_settings_or_config("DEVICE_UNIQUENESS") == DeviceUniquenessChoices.LOCATION_TENANT_NAME:
|
|
692
|
+
# Full natural key based on tenant, location, and name
|
|
693
|
+
return ["name", "tenant", "location"]
|
|
690
694
|
else:
|
|
691
|
-
|
|
692
|
-
return ["name", "tenant", "location"] # location should be last since it's potentially variadic
|
|
695
|
+
return ["pk"]
|
|
693
696
|
|
|
694
697
|
class Meta:
|
|
695
698
|
ordering = ("_name",) # Name may be null
|
|
696
699
|
unique_together = (
|
|
697
|
-
("location", "tenant", "name"), # See validate_unique below
|
|
698
700
|
("rack", "position", "face"),
|
|
699
701
|
("virtual_chassis", "vc_position"),
|
|
700
702
|
)
|
|
@@ -716,16 +718,6 @@ class Device(PrimaryModel, ConfigContextModel):
|
|
|
716
718
|
else:
|
|
717
719
|
self.clusters.set([cluster])
|
|
718
720
|
|
|
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
721
|
def clean(self):
|
|
730
722
|
from nautobot.ipam import models as ipam_models # circular import workaround
|
|
731
723
|
|
|
@@ -909,6 +901,11 @@ class Device(PrimaryModel, ConfigContextModel):
|
|
|
909
901
|
def save(self, *args, **kwargs):
|
|
910
902
|
is_new = not self.present_in_database
|
|
911
903
|
|
|
904
|
+
# to avoid circular import
|
|
905
|
+
from nautobot.dcim.custom_validators import DeviceUniquenessValidator
|
|
906
|
+
|
|
907
|
+
DeviceUniquenessValidator(self).clean()
|
|
908
|
+
|
|
912
909
|
super().save(*args, **kwargs)
|
|
913
910
|
|
|
914
911
|
# Apply any pending cluster assignment that was deferred during creation
|
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
|
@@ -295,6 +295,7 @@ class DeviceImportTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
295
295
|
|
|
296
296
|
class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
297
297
|
pk = ToggleColumn()
|
|
298
|
+
id = tables.Column(linkify=True, verbose_name="ID")
|
|
298
299
|
module_type = tables.Column(
|
|
299
300
|
linkify=lambda record: record.module_type.get_absolute_url(),
|
|
300
301
|
verbose_name="Type",
|
|
@@ -315,6 +316,7 @@ class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
315
316
|
model = Module
|
|
316
317
|
fields = (
|
|
317
318
|
"pk",
|
|
319
|
+
"id",
|
|
318
320
|
"module_type",
|
|
319
321
|
"module_family",
|
|
320
322
|
"parent_module_bay",
|
|
@@ -329,6 +331,7 @@ class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
329
331
|
)
|
|
330
332
|
default_columns = (
|
|
331
333
|
"pk",
|
|
334
|
+
"id",
|
|
332
335
|
"module_type",
|
|
333
336
|
"module_family",
|
|
334
337
|
"parent_module_bay",
|
|
@@ -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>
|
|
@@ -1,61 +1,2 @@
|
|
|
1
|
-
{% extends '
|
|
2
|
-
{%
|
|
3
|
-
|
|
4
|
-
{% block content_left_page %}
|
|
5
|
-
<div class="card">
|
|
6
|
-
<div class="card-header">
|
|
7
|
-
<strong>Cable</strong>
|
|
8
|
-
</div>
|
|
9
|
-
<table class="table table-hover card-body attr-table">
|
|
10
|
-
<tr>
|
|
11
|
-
<td>Type</td>
|
|
12
|
-
<td>{{ object.get_type_display|placeholder }}</td>
|
|
13
|
-
</tr>
|
|
14
|
-
<tr>
|
|
15
|
-
<td>Status</td>
|
|
16
|
-
<td>
|
|
17
|
-
{{ object.status| hyperlinked_object_with_color }}
|
|
18
|
-
</td>
|
|
19
|
-
</tr>
|
|
20
|
-
<tr>
|
|
21
|
-
<td>Label</td>
|
|
22
|
-
<td>{{ object.label|placeholder }}</td>
|
|
23
|
-
</tr>
|
|
24
|
-
<tr>
|
|
25
|
-
<td>Color</td>
|
|
26
|
-
<td>
|
|
27
|
-
{% if object.color %}
|
|
28
|
-
<span class="label nb-color-block" style="background-color: #{{ object.color }}"> </span>
|
|
29
|
-
{% else %}
|
|
30
|
-
<span class="text-secondary">—</span>
|
|
31
|
-
{% endif %}
|
|
32
|
-
</td>
|
|
33
|
-
</tr>
|
|
34
|
-
<tr>
|
|
35
|
-
<td>Length</td>
|
|
36
|
-
<td>
|
|
37
|
-
{% if object.length %}
|
|
38
|
-
{{ object.length }} {{ object.get_length_unit_display }}
|
|
39
|
-
{% else %}
|
|
40
|
-
<span class="text-secondary">—</span>
|
|
41
|
-
{% endif %}
|
|
42
|
-
</td>
|
|
43
|
-
</tr>
|
|
44
|
-
</table>
|
|
45
|
-
</div>
|
|
46
|
-
{% endblock content_left_page %}
|
|
47
|
-
|
|
48
|
-
{% block content_right_page %}
|
|
49
|
-
<div class="card">
|
|
50
|
-
<div class="card-header">
|
|
51
|
-
<strong>Termination A</strong>
|
|
52
|
-
</div>
|
|
53
|
-
{% include 'dcim/inc/cable_termination.html' with termination=object.termination_a %}
|
|
54
|
-
</div>
|
|
55
|
-
<div class="card">
|
|
56
|
-
<div class="card-header">
|
|
57
|
-
<strong>Termination B</strong>
|
|
58
|
-
</div>
|
|
59
|
-
{% include 'dcim/inc/cable_termination.html' with termination=object.termination_b %}
|
|
60
|
-
</div>
|
|
61
|
-
{% endblock content_right_page %}
|
|
1
|
+
{% extends 'dcim/cable_retrieve.html' %}
|
|
2
|
+
{% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
|
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
{% load helpers %}
|
|
4
4
|
{% load form_helpers %}
|
|
5
5
|
|
|
6
|
+
{% block title %}
|
|
7
|
+
{% with termination_a=form.instance.termination_a %}
|
|
8
|
+
{% if termination_a.module %}
|
|
9
|
+
Connect {{ termination_a.module.display }} {{ termination_a }} to {{ termination_b_type|bettertitle }}
|
|
10
|
+
{% else %}
|
|
11
|
+
Connect {{ termination_a.device }} {{ termination_a }} to {{ termination_b_type|bettertitle }}
|
|
12
|
+
{% endif %}
|
|
13
|
+
{% endwith %}
|
|
14
|
+
{% endblock %}
|
|
15
|
+
|
|
6
16
|
{% block content %}
|
|
7
17
|
<form method="post" class="h-100 vstack">
|
|
8
18
|
{% csrf_token %}
|
|
@@ -21,15 +31,6 @@
|
|
|
21
31
|
</div>
|
|
22
32
|
{% endif %}
|
|
23
33
|
{% with termination_a=form.instance.termination_a %}
|
|
24
|
-
<h3 class="mb-16">
|
|
25
|
-
{% block title %}
|
|
26
|
-
{% if termination_a.module %}
|
|
27
|
-
Connect {{ termination_a.module.display }} {{ termination_a }} to {{ termination_b_type|bettertitle }}
|
|
28
|
-
{% else %}
|
|
29
|
-
Connect {{ termination_a.device }} {{ termination_a }} to {{ termination_b_type|bettertitle }}
|
|
30
|
-
{% endif %}
|
|
31
|
-
{% endblock %}
|
|
32
|
-
</h3>
|
|
33
34
|
<div class="col-lg-5">
|
|
34
35
|
<div class="card">
|
|
35
36
|
<div class="card-header">
|
|
@@ -38,36 +39,11 @@
|
|
|
38
39
|
<div class="card-body">
|
|
39
40
|
{% if termination_a.device %}
|
|
40
41
|
{# Device component #}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
</div>
|
|
47
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
48
|
-
<label class="col-lg-3 col-form-label nb-required">Rack</label>
|
|
49
|
-
<div class="col-lg-9">
|
|
50
|
-
<p class="form-control-plaintext">{{ termination_a.device.rack|default:"None" }}</p>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
54
|
-
<label class="col-lg-3 col-form-label nb-required">Device</label>
|
|
55
|
-
<div class="col-lg-9">
|
|
56
|
-
<p class="form-control-plaintext">{{ termination_a.device }}</p>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
60
|
-
<label class="col-lg-3 col-form-label nb-required">Type</label>
|
|
61
|
-
<div class="col-lg-9">
|
|
62
|
-
<p class="form-control-plaintext">{{ termination_a|meta:"verbose_name"|bettertitle }}</p>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
66
|
-
<label class="col-lg-3 col-form-label nb-required">Name</label>
|
|
67
|
-
<div class="col-lg-9">
|
|
68
|
-
<p class="form-control-plaintext">{{ termination_a }}</p>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
42
|
+
{% include "inc/form_static_field.html" with label="Location" value=termination_a.device.location required=True %}
|
|
43
|
+
{% include "inc/form_static_field.html" with label="Rack" value=termination_a.device.rack|default:"None" required=True %}
|
|
44
|
+
{% include "inc/form_static_field.html" with label="Device" value=termination_a.device required=True %}
|
|
45
|
+
{% include "inc/form_static_field.html" with label="Type" value=termination_a|meta:"verbose_name"|bettertitle required=True %}
|
|
46
|
+
{% include "inc/form_static_field.html" with label="Name" value=termination_a required=True %}
|
|
71
47
|
{% elif termination_a.module %}
|
|
72
48
|
{# Module component #}
|
|
73
49
|
{% if not termination_a.module.device %}
|
|
@@ -75,76 +51,21 @@
|
|
|
75
51
|
This {{ termination_a|meta:"verbose_name"|bettertitle }} belongs
|
|
76
52
|
to a module not installed in any device.
|
|
77
53
|
</div>
|
|
78
|
-
|
|
79
|
-
<label class="col-lg-3 col-form-label nb-required">Location</label>
|
|
80
|
-
<div class="col-lg-9">
|
|
81
|
-
<p class="form-control-plaintext">{{ termination_a.module.location }}</p>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
54
|
+
{% include "inc/form_static_field.html" with label="Location" value=termination_a.module.location required=True %}
|
|
84
55
|
{% else %}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
92
|
-
<label class="col-lg-3 col-form-label nb-required">Rack</label>
|
|
93
|
-
<div class="col-lg-9">
|
|
94
|
-
<p class="form-control-plaintext">{{ termination_a.module.device.rack|default:"None" }}</p>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
98
|
-
<label class="col-lg-3 col-form-label nb-required">Device</label>
|
|
99
|
-
<div class="col-lg-9">
|
|
100
|
-
<p class="form-control-plaintext">{{ termination_a.module.device }}</p>
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
104
|
-
<label class="col-lg-3 col-form-label nb-required">Module</label>
|
|
105
|
-
<div class="col-lg-9">
|
|
106
|
-
<p class="form-control-plaintext">{{ termination_a.module.display }}</p>
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
56
|
+
{% include "inc/form_static_field.html" with label="Location" value=termination_a.module.device.location required=True %}
|
|
57
|
+
{% include "inc/form_static_field.html" with label="Rack" value=termination_a.module.device.rack|default:"None" required=True %}
|
|
58
|
+
{% include "inc/form_static_field.html" with label="Device" value=termination_a.module.device required=True %}
|
|
59
|
+
{% include "inc/form_static_field.html" with label="Module" value=termination_a.module.display required=True %}
|
|
109
60
|
{% endif %}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<div class="col-lg-9">
|
|
113
|
-
<p class="form-control-plaintext">{{ termination_a|meta:"verbose_name"|bettertitle }}</p>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
117
|
-
<label class="col-lg-3 col-form-label nb-required">Name</label>
|
|
118
|
-
<div class="col-lg-9">
|
|
119
|
-
<p class="form-control-plaintext">{{ termination_a }}</p>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
61
|
+
{% include "inc/form_static_field.html" with label="Type" value=termination_a|meta:"verbose_name"|bettertitle required=True %}
|
|
62
|
+
{% include "inc/form_static_field.html" with label="Name" value=termination_a required=True %}
|
|
122
63
|
{% else %}
|
|
123
64
|
{# Circuit termination #}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
131
|
-
<label class="col-lg-3 col-form-label nb-required">Provider</label>
|
|
132
|
-
<div class="col-lg-9">
|
|
133
|
-
<p class="form-control-plaintext">{{ termination_a.circuit.provider }}</p>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
137
|
-
<label class="col-lg-3 col-form-label nb-required">Circuit</label>
|
|
138
|
-
<div class="col-lg-9">
|
|
139
|
-
<p class="form-control-plaintext">{{ termination_a.circuit.cid }}</p>
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
<div class="mb-10 d-flex justify-content-center">
|
|
143
|
-
<label class="col-lg-3 col-form-label nb-required">Side</label>
|
|
144
|
-
<div class="col-lg-9">
|
|
145
|
-
<p class="form-control-plaintext">{{ termination_a.term_side }}</p>
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
65
|
+
{% include "inc/form_static_field.html" with label="Location" value=termination_a.location required=True %}
|
|
66
|
+
{% include "inc/form_static_field.html" with label="Provider" value=termination_a.circuit.provider required=True %}
|
|
67
|
+
{% include "inc/form_static_field.html" with label="Circuit" value=termination_a.circuit.cid required=True %}
|
|
68
|
+
{% include "inc/form_static_field.html" with label="Side" value=termination_a.term_side required=True %}
|
|
148
69
|
{% endif %}
|
|
149
70
|
</div>
|
|
150
71
|
</div>
|
|
@@ -192,17 +113,12 @@
|
|
|
192
113
|
{% if 'termination_b_circuit' in form.fields %}
|
|
193
114
|
{% render_field form.termination_b_circuit %}
|
|
194
115
|
{% endif %}
|
|
195
|
-
|
|
196
|
-
<label class="col-lg-3 col-form-label nb-required">Type</label>
|
|
197
|
-
<div class="col-lg-9">
|
|
198
|
-
<p class="form-control-plaintext">{{ termination_b_type|bettertitle }}</p>
|
|
199
|
-
</div>
|
|
200
|
-
</div>
|
|
116
|
+
{% include "inc/form_static_field.html" with label="Type" value=termination_b_type|bettertitle required=True %}
|
|
201
117
|
{% render_field form.termination_b_id %}
|
|
202
118
|
</div>
|
|
203
119
|
</div>
|
|
204
120
|
</div>
|
|
205
|
-
<div class="col-lg-6 offset-
|
|
121
|
+
<div class="col-lg-6 offset-lg-3">
|
|
206
122
|
{% include 'dcim/inc/cable_form.html' %}
|
|
207
123
|
</div>
|
|
208
124
|
{% endwith %}
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
{% extends '
|
|
2
|
-
|
|
3
|
-
{% block form %}
|
|
4
|
-
{% include 'dcim/inc/cable_form.html' %}
|
|
5
|
-
{% endblock %}
|
|
1
|
+
{% extends 'dcim/cable_update.html' %}
|
|
2
|
+
{% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
|