nautobot 2.4.17__py3-none-any.whl → 2.4.19__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/ui.py +6 -0
- nautobot/apps/views.py +2 -0
- nautobot/circuits/tables.py +1 -1
- nautobot/circuits/templates/circuits/circuit_create.html +7 -7
- nautobot/circuits/templates/circuits/circuit_retrieve.html +1 -5
- nautobot/circuits/templates/circuits/circuittermination_create.html +26 -26
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -8
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +20 -20
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +3 -3
- nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +9 -0
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +1 -3
- nautobot/circuits/tests/integration/test_circuit.py +2 -2
- nautobot/circuits/views.py +49 -15
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +1 -4
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +1 -7
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +1 -3
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +1 -5
- nautobot/cloud/views.py +45 -0
- nautobot/core/filters.py +2 -2
- nautobot/core/graphql/generators.py +5 -2
- nautobot/core/jobs/bulk_actions.py +48 -85
- nautobot/core/models/querysets.py +2 -1
- nautobot/core/settings.py +1 -0
- nautobot/core/settings.yaml +9 -0
- nautobot/core/tables.py +21 -23
- nautobot/core/templates/40x.html +15 -15
- nautobot/core/templates/500.html +21 -21
- nautobot/core/templates/admin/app_index.html +8 -8
- nautobot/core/templates/admin/base.html +104 -104
- nautobot/core/templates/admin/change_form.html +65 -65
- nautobot/core/templates/admin/change_list.html +60 -60
- nautobot/core/templates/admin/change_list_results.html +39 -39
- nautobot/core/templates/admin/config/config.html +47 -47
- nautobot/core/templates/admin/delete_confirmation.html +47 -47
- nautobot/core/templates/admin/edit_inline/stacked.html +124 -124
- nautobot/core/templates/admin/edit_inline/tabular.html +60 -60
- nautobot/core/templates/admin/includes/fieldset.html +4 -4
- nautobot/core/templates/admin/index.html +60 -60
- nautobot/core/templates/admin/prepopulated_fields_js.html +18 -18
- nautobot/core/templates/admin/submit_line.html +4 -4
- nautobot/core/templates/base_django.html +46 -46
- nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +8 -8
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +8 -8
- nautobot/core/templates/buttons/export.html +3 -3
- nautobot/core/templates/components/breadcrumbs.html +19 -0
- nautobot/core/templates/components/button/default.html +3 -3
- nautobot/core/templates/components/button/dropdown.html +7 -7
- nautobot/core/templates/components/button/formbutton.html +4 -4
- nautobot/core/templates/components/panel/body_content_data_table.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +3 -0
- nautobot/core/templates/components/panel/footer_content_table.html +3 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +10 -1
- nautobot/core/templates/components/tab/content_wrapper.html +1 -1
- nautobot/core/templates/components/tab/label_wrapper.html +1 -1
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +10 -3
- nautobot/core/templates/generic/object_bulk_add_component.html +40 -40
- nautobot/core/templates/generic/object_bulk_create.html +3 -3
- nautobot/core/templates/generic/object_bulk_destroy.html +6 -6
- nautobot/core/templates/generic/object_bulk_update.html +52 -52
- nautobot/core/templates/generic/object_changelog.html +0 -2
- nautobot/core/templates/generic/object_import.html +33 -33
- nautobot/core/templates/generic/object_list.html +271 -268
- nautobot/core/templates/generic/object_notes.html +0 -2
- nautobot/core/templates/generic/object_retrieve.html +264 -257
- nautobot/core/templates/graphene/graphiql.html +127 -127
- nautobot/core/templates/home.html +62 -62
- nautobot/core/templates/inc/computed_fields/panel_data.html +13 -13
- nautobot/core/templates/inc/created_updated.html +8 -8
- nautobot/core/templates/inc/custom_fields/panel_data.html +13 -13
- nautobot/core/templates/inc/dynamic_groups_panel.html +11 -11
- nautobot/core/templates/inc/footer.html +19 -19
- nautobot/core/templates/inc/javascript.html +1 -1
- nautobot/core/templates/inc/media.html +46 -46
- nautobot/core/templates/inc/nav_menu.html +1 -1
- nautobot/core/templates/inc/relationships_table_rows.html +22 -22
- nautobot/core/templates/inc/tenant_table_row.html +1 -1
- nautobot/core/templates/login.html +77 -77
- nautobot/core/templates/media_failure.html +38 -38
- nautobot/core/templates/panel_table.html +1 -1
- nautobot/core/templates/rest_framework/api.html +3 -3
- nautobot/core/templates/search.html +1 -1
- nautobot/core/templates/swagger_ui.html +9 -9
- nautobot/core/templates/utilities/confirmation_form.html +18 -18
- nautobot/core/templates/utilities/render_field.html +1 -1
- nautobot/core/templates/utilities/render_jinja2.html +43 -43
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +56 -56
- nautobot/core/templates/utilities/templatetags/utilization_graph.html +1 -1
- nautobot/core/templates/utilities/theme_preview.html +799 -799
- nautobot/core/templates/utilities/worker_status.html +122 -122
- nautobot/core/templates/widgets/clearable_file.html +3 -3
- nautobot/core/templates/widgets/sluginput.html +1 -1
- nautobot/core/templatetags/buttons.py +8 -2
- nautobot/core/templatetags/helpers.py +24 -0
- nautobot/core/templatetags/ui_framework.py +40 -5
- nautobot/core/testing/filters.py +37 -21
- nautobot/core/testing/integration.py +7 -4
- nautobot/core/testing/views.py +49 -5
- nautobot/core/tests/test_breadcrumbs.py +78 -4
- nautobot/core/tests/test_commands.py +7 -4
- nautobot/core/tests/test_graphql.py +20 -5
- nautobot/core/tests/test_jobs.py +34 -21
- nautobot/core/tests/test_tables.py +43 -6
- nautobot/core/tests/test_templatetags_ui_framework.py +146 -0
- nautobot/core/tests/test_titles.py +2 -2
- nautobot/core/tests/test_ui.py +188 -1
- nautobot/core/tests/test_utils.py +35 -0
- nautobot/core/tests/test_views.py +45 -0
- nautobot/core/tests/test_views_generic.py +43 -0
- nautobot/core/tests/test_views_utils.py +239 -5
- nautobot/core/ui/breadcrumbs.py +220 -28
- nautobot/core/ui/bulk_buttons.py +8 -0
- nautobot/core/ui/object_detail.py +181 -60
- nautobot/core/ui/titles.py +10 -5
- nautobot/core/utils/requests.py +27 -2
- nautobot/core/views/__init__.py +24 -3
- nautobot/core/views/generic.py +70 -35
- nautobot/core/views/mixins.py +226 -122
- nautobot/core/views/utils.py +270 -1
- nautobot/dcim/api/serializers.py +8 -2
- nautobot/dcim/constants.py +1 -0
- nautobot/dcim/factory.py +4 -3
- nautobot/dcim/filters/mixins.py +1 -2
- nautobot/dcim/forms.py +5 -1
- nautobot/dcim/migrations/0074_alter_rack_u_height.py +21 -0
- nautobot/dcim/models/devices.py +30 -1
- nautobot/dcim/models/racks.py +2 -2
- nautobot/dcim/tables/__init__.py +2 -0
- nautobot/dcim/tables/devices.py +24 -0
- nautobot/dcim/tables/power.py +2 -2
- nautobot/dcim/templates/dcim/cable.html +53 -53
- nautobot/dcim/templates/dcim/cable_connect.html +182 -182
- nautobot/dcim/templates/dcim/cable_trace.html +1 -1
- nautobot/dcim/templates/dcim/console_port_connection_list.html +5 -5
- nautobot/dcim/templates/dcim/consoleport.html +86 -86
- nautobot/dcim/templates/dcim/consoleserverport.html +86 -86
- nautobot/dcim/templates/dcim/controller_create.html +34 -34
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -68
- nautobot/dcim/templates/dcim/device/base.html +1 -114
- nautobot/dcim/templates/dcim/device/config.html +17 -17
- nautobot/dcim/templates/dcim/device/consoleports.html +1 -52
- nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -52
- nautobot/dcim/templates/dcim/device/devicebays.html +1 -48
- nautobot/dcim/templates/dcim/device/frontports.html +1 -52
- nautobot/dcim/templates/dcim/device/interfaces.html +1 -56
- nautobot/dcim/templates/dcim/device/inventory.html +1 -48
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +64 -64
- nautobot/dcim/templates/dcim/device/modulebays.html +1 -48
- nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -52
- nautobot/dcim/templates/dcim/device/powerports.html +1 -52
- nautobot/dcim/templates/dcim/device/rearports.html +1 -52
- nautobot/dcim/templates/dcim/device/status.html +66 -66
- nautobot/dcim/templates/dcim/device/wireless.html +1 -72
- nautobot/dcim/templates/dcim/device.html +4 -422
- nautobot/dcim/templates/dcim/device_component.html +0 -19
- nautobot/dcim/templates/dcim/device_component_add.html +25 -25
- nautobot/dcim/templates/dcim/device_create.html +229 -0
- nautobot/dcim/templates/dcim/device_edit.html +2 -227
- nautobot/dcim/templates/dcim/devicebay.html +41 -41
- nautobot/dcim/templates/dcim/devicebay_populate.html +32 -32
- nautobot/dcim/templates/dcim/devicetype_component_add.html +28 -28
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +1 -3
- nautobot/dcim/templates/dcim/frontport.html +84 -84
- nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
- nautobot/dcim/templates/dcim/inc/device_interface_filter.html +8 -0
- nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +1 -15
- nautobot/dcim/templates/dcim/inc/location_hierarchy.html +22 -22
- nautobot/dcim/templates/dcim/interface.html +206 -206
- nautobot/dcim/templates/dcim/interface_connection_list.html +5 -5
- nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +6 -6
- nautobot/dcim/templates/dcim/inventoryitem.html +44 -44
- nautobot/dcim/templates/dcim/inventoryitem_add.html +32 -32
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +22 -22
- nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +46 -46
- nautobot/dcim/templates/dcim/location_retrieve.html +1 -7
- nautobot/dcim/templates/dcim/locationtype.html +1 -6
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -7
- nautobot/dcim/templates/dcim/module/base.html +85 -85
- nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
- nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
- nautobot/dcim/templates/dcim/module_retrieve.html +52 -52
- nautobot/dcim/templates/dcim/module_update.html +61 -61
- nautobot/dcim/templates/dcim/modulebay_destroy.html +1 -1
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +83 -99
- nautobot/dcim/templates/dcim/modulebay_update.html +33 -33
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +140 -144
- nautobot/dcim/templates/dcim/platform_create.html +38 -38
- nautobot/dcim/templates/dcim/power_port_connection_list.html +5 -5
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -8
- nautobot/dcim/templates/dcim/poweroutlet.html +85 -85
- nautobot/dcim/templates/dcim/powerpanel_retrieve.html +1 -8
- nautobot/dcim/templates/dcim/powerport.html +91 -91
- nautobot/dcim/templates/dcim/rack_elevation_list.html +18 -18
- nautobot/dcim/templates/dcim/rack_retrieve.html +264 -274
- nautobot/dcim/templates/dcim/rackreservation_retrieve.html +0 -3
- nautobot/dcim/templates/dcim/rearport.html +78 -78
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -50
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -5
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +3 -2
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +6 -2
- nautobot/dcim/tests/test_api.py +33 -1
- nautobot/dcim/tests/test_views.py +189 -4
- nautobot/dcim/ui.py +29 -0
- nautobot/dcim/urls.py +1 -109
- nautobot/dcim/utils.py +30 -0
- nautobot/dcim/views.py +1149 -550
- nautobot/extras/filters/mixins.py +1 -1
- nautobot/extras/forms/forms.py +15 -0
- nautobot/extras/models/groups.py +10 -1
- nautobot/extras/models/jobs.py +2 -2
- nautobot/extras/plugins/views.py +18 -5
- nautobot/extras/tables.py +24 -2
- nautobot/extras/templates/extras/computedfield_edit.html +4 -4
- nautobot/extras/templates/extras/configcontext_update.html +1 -1
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +32 -32
- nautobot/extras/templates/extras/customfield_retrieve.html +1 -128
- nautobot/extras/templates/extras/customfield_update.html +23 -23
- nautobot/extras/templates/extras/dynamicgroup.html +2 -99
- nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -199
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +99 -0
- nautobot/extras/templates/extras/dynamicgroup_update.html +199 -0
- nautobot/extras/templates/extras/gitrepository.html +2 -82
- nautobot/extras/templates/extras/gitrepository_list.html +10 -10
- nautobot/extras/templates/extras/gitrepository_object_edit.html +2 -13
- nautobot/extras/templates/extras/gitrepository_retrieve.html +82 -0
- nautobot/extras/templates/extras/gitrepository_update.html +13 -0
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +73 -73
- nautobot/extras/templates/extras/inc/configcontext_format.html +2 -2
- nautobot/extras/templates/extras/inc/job_table.html +10 -10
- nautobot/extras/templates/extras/inc/jobresult.html +21 -21
- nautobot/extras/templates/extras/inc/jobresult_js.html +6 -6
- nautobot/extras/templates/extras/inc/tags_panel.html +10 -10
- nautobot/extras/templates/extras/job.html +64 -64
- nautobot/extras/templates/extras/job_approval_request.html +9 -9
- nautobot/extras/templates/extras/job_bulk_edit.html +13 -13
- nautobot/extras/templates/extras/job_edit.html +45 -45
- nautobot/extras/templates/extras/job_list.html +4 -4
- nautobot/extras/templates/extras/jobresult_retrieve.html +0 -25
- nautobot/extras/templates/extras/marketplace.html +101 -101
- nautobot/extras/templates/extras/metadatatype_create.html +20 -20
- nautobot/extras/templates/extras/note_retrieve.html +0 -52
- nautobot/extras/templates/extras/object_assign_contact_or_team.html +18 -18
- nautobot/extras/templates/extras/object_configcontext.html +1 -3
- nautobot/extras/templates/extras/objectchange.html +2 -165
- nautobot/extras/templates/extras/objectchange_retrieve.html +165 -0
- nautobot/extras/templates/extras/plugin_detail.html +44 -48
- nautobot/extras/templates/extras/plugins_list.html +9 -11
- nautobot/extras/templates/extras/plugins_tiles.html +26 -26
- nautobot/extras/templates/extras/relationship_edit.html +4 -4
- nautobot/extras/templates/extras/role_retrieve.html +13 -13
- nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +21 -21
- nautobot/extras/templates/extras/scheduledjob.html +128 -128
- nautobot/extras/templates/extras/secret_create.html +53 -53
- nautobot/extras/templates/extras/secretsgroup_update.html +13 -13
- nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +3 -3
- nautobot/extras/templates/extras/webhook.html +79 -79
- nautobot/extras/tests/integration/test_relationships.py +6 -6
- nautobot/extras/tests/test_dynamicgroups.py +73 -18
- nautobot/extras/tests/test_filters.py +1 -1
- nautobot/extras/tests/test_jobs.py +2 -0
- nautobot/extras/tests/test_views.py +8 -3
- nautobot/extras/urls.py +3 -97
- nautobot/extras/views.py +524 -456
- nautobot/ipam/filters.py +2 -2
- nautobot/ipam/migrations/0053_alter_vrfdeviceassignment_options_and_more.py +20 -0
- nautobot/ipam/models.py +34 -0
- nautobot/ipam/querysets.py +3 -3
- nautobot/ipam/signals.py +6 -1
- nautobot/ipam/tables.py +3 -1
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +4 -0
- nautobot/ipam/templates/ipam/inc/toggle_available.html +8 -8
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +4 -4
- nautobot/ipam/templates/ipam/ipaddress.html +119 -123
- nautobot/ipam/templates/ipam/ipaddress_assign.html +10 -10
- nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -1
- nautobot/ipam/templates/ipam/ipaddress_merge.html +180 -180
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +48 -48
- nautobot/ipam/templates/ipam/prefix.html +2 -115
- nautobot/ipam/templates/ipam/prefix_create.html +34 -0
- nautobot/ipam/templates/ipam/prefix_edit.html +1 -34
- nautobot/ipam/templates/ipam/prefix_retrieve.html +3 -0
- nautobot/ipam/templates/ipam/service_retrieve.html +1 -6
- nautobot/ipam/templates/ipam/vlan_retrieve.html +1 -7
- nautobot/ipam/templates/ipam/vrf_edit.html +1 -1
- nautobot/ipam/tests/test_api.py +5 -0
- nautobot/ipam/tests/test_models.py +387 -0
- nautobot/ipam/tests/test_querysets.py +46 -0
- nautobot/ipam/tests/test_views.py +34 -0
- nautobot/ipam/ui.py +145 -0
- nautobot/ipam/urls.py +1 -46
- nautobot/ipam/utils/__init__.py +26 -0
- nautobot/ipam/utils/migrations.py +1 -1
- nautobot/ipam/views.py +234 -112
- nautobot/project-static/docs/404.html +11 -11
- nautobot/project-static/docs/apps/index.html +11 -11
- nautobot/project-static/docs/apps/nautobot-apps.html +11 -11
- nautobot/project-static/docs/assets/javascripts/{bundle.92b07e13.min.js → bundle.f55a23d4.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.92b07e13.min.js.map → bundle.f55a23d4.min.js.map} +2 -2
- nautobot/project-static/docs/assets/stylesheets/{main.7e37652d.min.css → main.e53b48f4.min.css} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.7e37652d.min.css.map → main.e53b48f4.min.css.map} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +83 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1265 -281
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +12 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +452 -29
- nautobot/project-static/docs/development/apps/api/configuration-view.html +11 -11
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +11 -11
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +11 -11
- nautobot/project-static/docs/development/apps/api/models/global-search.html +11 -11
- nautobot/project-static/docs/development/apps/api/models/graphql.html +11 -11
- nautobot/project-static/docs/development/apps/api/models/index.html +11 -11
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +12 -12
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/prepopulating-data.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +11 -11
- nautobot/project-static/docs/development/apps/api/prometheus.html +11 -11
- nautobot/project-static/docs/development/apps/api/setup.html +11 -11
- nautobot/project-static/docs/development/apps/api/testing.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/base-template.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/index.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/notes.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/urls.html +11 -11
- nautobot/project-static/docs/development/apps/index.html +11 -11
- nautobot/project-static/docs/development/apps/migration/code-updates.html +11 -11
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +11 -11
- nautobot/project-static/docs/development/apps/migration/from-v1.html +11 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +11 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +11 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +11 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +11 -11
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +11 -11
- nautobot/project-static/docs/development/core/application-registry.html +11 -11
- nautobot/project-static/docs/development/core/best-practices.html +11 -11
- nautobot/project-static/docs/development/core/bootstrap-ui.html +11 -11
- nautobot/project-static/docs/development/core/caching.html +11 -11
- nautobot/project-static/docs/development/core/controllers.html +11 -11
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +11 -11
- nautobot/project-static/docs/development/core/generic-views.html +11 -11
- nautobot/project-static/docs/development/core/getting-started.html +50 -63
- nautobot/project-static/docs/development/core/homepage.html +11 -11
- nautobot/project-static/docs/development/core/index.html +11 -11
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +11 -11
- nautobot/project-static/docs/development/core/model-checklist.html +11 -11
- nautobot/project-static/docs/development/core/model-features.html +11 -11
- nautobot/project-static/docs/development/core/natural-keys.html +11 -11
- nautobot/project-static/docs/development/core/navigation-menu.html +11 -11
- nautobot/project-static/docs/development/core/release-checklist.html +11 -11
- nautobot/project-static/docs/development/core/role-internals.html +11 -11
- nautobot/project-static/docs/development/core/settings.html +11 -11
- nautobot/project-static/docs/development/core/style-guide.html +15 -11
- nautobot/project-static/docs/development/core/templates.html +11 -11
- nautobot/project-static/docs/development/core/testing.html +11 -11
- nautobot/project-static/docs/development/core/ui-component-framework.html +17 -22
- nautobot/project-static/docs/development/core/user-preferences.html +11 -11
- nautobot/project-static/docs/development/index.html +11 -11
- nautobot/project-static/docs/development/jobs/getting-started.html +11 -11
- nautobot/project-static/docs/development/jobs/index.html +11 -11
- nautobot/project-static/docs/development/jobs/installation.html +11 -11
- nautobot/project-static/docs/development/jobs/job-extensions.html +11 -11
- nautobot/project-static/docs/development/jobs/job-logging.html +11 -11
- nautobot/project-static/docs/development/jobs/job-patterns.html +11 -11
- nautobot/project-static/docs/development/jobs/job-structure.html +11 -11
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +11 -11
- nautobot/project-static/docs/development/jobs/testing.html +11 -11
- nautobot/project-static/docs/index.html +11 -11
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +11 -11
- nautobot/project-static/docs/overview/design_philosophy.html +11 -11
- nautobot/project-static/docs/release-notes/index.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.0.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.1.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.2.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.3.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.4.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.5.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.6.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.0.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.1.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.2.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.3.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.4.html +418 -11
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +300 -300
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +38 -11
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +89 -14
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/index.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/services.html +11 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +11 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +11 -11
- nautobot/project-static/docs/user-guide/administration/security/index.html +11 -11
- nautobot/project-static/docs/user-guide/administration/security/notices.html +11 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +11 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +11 -11
- nautobot/project-static/docs/user-guide/index.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +11 -11
- nautobot/project-static/img/nautobot_icon.svg +32 -34
- nautobot/project-static/js/table_sorting_indicator.js +0 -2
- nautobot/tenancy/templates/tenancy/tenant.html +1 -7
- nautobot/tenancy/views.py +13 -0
- nautobot/users/templates/users/api_tokens.html +4 -4
- nautobot/users/templates/users/base.html +28 -28
- nautobot/virtualization/templates/virtualization/cluster.html +64 -64
- nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +8 -0
- nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +25 -25
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +1 -251
- nautobot/virtualization/templates/virtualization/vminterface.html +70 -70
- nautobot/virtualization/urls.py +0 -12
- nautobot/virtualization/views.py +158 -54
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +13 -13
- nautobot/wireless/tests/integration/test_radio_profile.py +1 -1
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/METADATA +4 -4
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/RECORD +623 -607
- nautobot/core/templates/inc/breadcrumbs.html +0 -14
- nautobot/ipam/templates/ipam/prefix_ipaddresses.html +0 -11
- nautobot/ipam/templates/ipam/prefix_prefixes.html +0 -11
- nautobot/project-static/docs/requirements.txt +0 -14
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/NOTICE +0 -0
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/WHEEL +0 -0
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/entry_points.txt +0 -0
nautobot/dcim/views.py
CHANGED
|
@@ -16,12 +16,12 @@ from django.forms import (
|
|
|
16
16
|
MultipleHiddenInput,
|
|
17
17
|
)
|
|
18
18
|
from django.shortcuts import get_object_or_404, HttpResponse, redirect, render
|
|
19
|
+
from django.template import Context
|
|
19
20
|
from django.template.loader import render_to_string
|
|
20
21
|
from django.urls import reverse
|
|
21
22
|
from django.utils.encoding import iri_to_uri
|
|
22
|
-
from django.utils.
|
|
23
|
-
from django.utils.
|
|
24
|
-
from django.utils.http import url_has_allowed_host_and_scheme
|
|
23
|
+
from django.utils.html import format_html, mark_safe
|
|
24
|
+
from django.utils.http import url_has_allowed_host_and_scheme, urlencode
|
|
25
25
|
from django.views.generic import View
|
|
26
26
|
from django_tables2 import RequestConfig
|
|
27
27
|
from rest_framework.decorators import action
|
|
@@ -38,17 +38,23 @@ from nautobot.core.templatetags import helpers
|
|
|
38
38
|
from nautobot.core.templatetags.helpers import has_perms
|
|
39
39
|
from nautobot.core.ui import object_detail
|
|
40
40
|
from nautobot.core.ui.breadcrumbs import (
|
|
41
|
+
AncestorsBreadcrumbs,
|
|
41
42
|
BaseBreadcrumbItem,
|
|
42
43
|
Breadcrumbs,
|
|
44
|
+
context_object_attr,
|
|
43
45
|
InstanceBreadcrumbItem,
|
|
46
|
+
InstanceParentBreadcrumbItem,
|
|
44
47
|
ModelBreadcrumbItem,
|
|
48
|
+
ViewNameBreadcrumbItem,
|
|
45
49
|
)
|
|
46
50
|
from nautobot.core.ui.bulk_buttons import (
|
|
47
51
|
BulkDeleteButton,
|
|
52
|
+
BulkDisconnectButton,
|
|
48
53
|
BulkEditButton,
|
|
49
54
|
BulkRenameButton,
|
|
50
55
|
)
|
|
51
56
|
from nautobot.core.ui.choices import SectionChoices
|
|
57
|
+
from nautobot.core.ui.titles import Titles
|
|
52
58
|
from nautobot.core.utils.lookup import get_form_for_model
|
|
53
59
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
54
60
|
from nautobot.core.utils.requests import normalize_querydict
|
|
@@ -66,23 +72,26 @@ from nautobot.core.views.mixins import (
|
|
|
66
72
|
ObjectPermissionRequiredMixin,
|
|
67
73
|
)
|
|
68
74
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
69
|
-
from nautobot.core.views.utils import get_obj_from_context
|
|
75
|
+
from nautobot.core.views.utils import common_detail_view_context, get_obj_from_context
|
|
70
76
|
from nautobot.core.views.viewsets import NautobotUIViewSet
|
|
71
77
|
from nautobot.dcim.choices import LocationDataToContactActionChoices
|
|
72
78
|
from nautobot.dcim.forms import LocationMigrateDataToContactForm
|
|
73
|
-
from nautobot.dcim.
|
|
74
|
-
from nautobot.
|
|
75
|
-
from nautobot.extras.
|
|
76
|
-
from nautobot.extras.
|
|
77
|
-
from nautobot.ipam.models import IPAddress, Prefix,
|
|
78
|
-
from nautobot.ipam.tables import
|
|
79
|
+
from nautobot.dcim.ui import RackBreadcrumbs
|
|
80
|
+
from nautobot.dcim.utils import get_all_network_driver_mappings, render_software_version_and_image_files
|
|
81
|
+
from nautobot.extras.models import ConfigContext, Contact, ContactAssociation, Role, Status, Team
|
|
82
|
+
from nautobot.extras.tables import DynamicGroupTable, ImageAttachmentTable
|
|
83
|
+
from nautobot.ipam.models import IPAddress, Prefix, VLAN
|
|
84
|
+
from nautobot.ipam.tables import (
|
|
85
|
+
InterfaceIPAddressTable,
|
|
86
|
+
InterfaceVLANTable,
|
|
87
|
+
ServiceTable,
|
|
88
|
+
VRFDeviceAssignmentTable,
|
|
89
|
+
VRFTable,
|
|
90
|
+
)
|
|
91
|
+
from nautobot.ipam.utils import render_ip_with_nat
|
|
79
92
|
from nautobot.virtualization.models import VirtualMachine
|
|
80
93
|
from nautobot.virtualization.tables import VirtualMachineTable
|
|
81
94
|
from nautobot.wireless.forms import ControllerManagedDeviceGroupWirelessNetworkFormSet
|
|
82
|
-
from nautobot.wireless.models import (
|
|
83
|
-
ControllerManagedDeviceGroupRadioProfileAssignment,
|
|
84
|
-
ControllerManagedDeviceGroupWirelessNetworkAssignment,
|
|
85
|
-
)
|
|
86
95
|
from nautobot.wireless.tables import (
|
|
87
96
|
BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable,
|
|
88
97
|
ControllerManagedDeviceGroupRadioProfileAssignmentTable,
|
|
@@ -238,6 +247,7 @@ class LocationTypeUIViewSet(NautobotUIViewSet):
|
|
|
238
247
|
form_class = forms.LocationTypeForm
|
|
239
248
|
bulk_update_form_class = forms.LocationTypeBulkEditForm
|
|
240
249
|
serializer_class = serializers.LocationSerializer
|
|
250
|
+
breadcrumbs = AncestorsBreadcrumbs(detail_item_label=context_object_attr("name"))
|
|
241
251
|
|
|
242
252
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
243
253
|
panels=(
|
|
@@ -281,28 +291,36 @@ class LocationUIViewSet(NautobotUIViewSet):
|
|
|
281
291
|
form_class = forms.LocationForm
|
|
282
292
|
bulk_update_form_class = forms.LocationBulkEditForm
|
|
283
293
|
serializer_class = serializers.LocationSerializer
|
|
294
|
+
breadcrumbs = AncestorsBreadcrumbs(detail_item_label=context_object_attr("name"))
|
|
284
295
|
|
|
285
296
|
def get_extra_context(self, request, instance):
|
|
286
297
|
if instance is None:
|
|
287
298
|
return super().get_extra_context(request, instance)
|
|
288
|
-
|
|
299
|
+
# This query can get really expensive when there are big location trees in the DB. By casting it to a list we
|
|
300
|
+
# ensure it is only performed once rather than as a subquery for each of the different count stats.
|
|
301
|
+
related_locations = list(
|
|
289
302
|
instance.descendants(include_self=True).restrict(request.user, "view").values_list("pk", flat=True)
|
|
290
303
|
)
|
|
304
|
+
prefix_count_queryset = Prefix.objects.restrict(request.user, "view").filter(locations__in=related_locations)
|
|
305
|
+
vlan_count_queryset = VLAN.objects.restrict(request.user, "view").filter(locations__in=related_locations)
|
|
306
|
+
circuit_count_queryset = Circuit.objects.restrict(request.user, "view").filter(
|
|
307
|
+
circuit_terminations__location__in=related_locations
|
|
308
|
+
)
|
|
309
|
+
# When there is more than one location, the models that can be assigned to more then one location at the same
|
|
310
|
+
# time need to be queried with `distinct`. We are avoiding `distinct` when this is not the case, as it incurs
|
|
311
|
+
# a performance penalty.
|
|
312
|
+
if len(related_locations) > 1:
|
|
313
|
+
prefix_count_queryset = prefix_count_queryset.distinct()
|
|
314
|
+
vlan_count_queryset = vlan_count_queryset.distinct()
|
|
315
|
+
circuit_count_queryset = circuit_count_queryset.distinct()
|
|
291
316
|
stats = {
|
|
317
|
+
"prefix_count": prefix_count_queryset.count(),
|
|
318
|
+
"vlan_count": vlan_count_queryset.count(),
|
|
319
|
+
"circuit_count": circuit_count_queryset.count(),
|
|
292
320
|
"rack_count": Rack.objects.restrict(request.user, "view").filter(location__in=related_locations).count(),
|
|
293
321
|
"device_count": Device.objects.restrict(request.user, "view")
|
|
294
322
|
.filter(location__in=related_locations)
|
|
295
323
|
.count(),
|
|
296
|
-
"prefix_count": Prefix.objects.restrict(request.user, "view")
|
|
297
|
-
.filter(locations__in=related_locations)
|
|
298
|
-
.count(),
|
|
299
|
-
"vlan_count": VLAN.objects.restrict(request.user, "view")
|
|
300
|
-
.filter(locations__in=related_locations)
|
|
301
|
-
.distinct()
|
|
302
|
-
.count(),
|
|
303
|
-
"circuit_count": Circuit.objects.restrict(request.user, "view")
|
|
304
|
-
.filter(circuit_terminations__location__in=related_locations)
|
|
305
|
-
.count(),
|
|
306
324
|
"vm_count": VirtualMachine.objects.restrict(request.user, "view")
|
|
307
325
|
.filter(cluster__location__in=related_locations)
|
|
308
326
|
.count(),
|
|
@@ -515,6 +533,7 @@ class RackGroupUIViewSet(NautobotUIViewSet):
|
|
|
515
533
|
if self.action == "retrieve" and instance:
|
|
516
534
|
racks = (
|
|
517
535
|
Rack.objects.restrict(request.user, "view")
|
|
536
|
+
# Note this filter - we want the table to include racks assigned to child rack groups as well
|
|
518
537
|
.filter(rack_group__in=instance.descendants(include_self=True))
|
|
519
538
|
.select_related("role", "location", "tenant")
|
|
520
539
|
)
|
|
@@ -546,6 +565,7 @@ class RackUIViewSet(NautobotUIViewSet):
|
|
|
546
565
|
serializer_class = serializers.RackSerializer
|
|
547
566
|
table_class = tables.RackDetailTable
|
|
548
567
|
queryset = Rack.objects.select_related("location", "tenant__tenant_group", "rack_group", "role")
|
|
568
|
+
breadcrumbs = RackBreadcrumbs()
|
|
549
569
|
|
|
550
570
|
def get_extra_context(self, request, instance):
|
|
551
571
|
context = super().get_extra_context(request, instance)
|
|
@@ -590,6 +610,10 @@ class RackElevationListView(generic.ObjectListView):
|
|
|
590
610
|
filterset_form = forms.RackFilterForm
|
|
591
611
|
action_buttons = []
|
|
592
612
|
template_name = "dcim/rack_elevation_list.html"
|
|
613
|
+
view_titles = Titles(titles={"list": "Rack Elevation"})
|
|
614
|
+
breadcrumbs = Breadcrumbs(
|
|
615
|
+
items={"list": [ViewNameBreadcrumbItem(view_name="dcim:rack_elevation_list", label="Rack Elevation")]}
|
|
616
|
+
)
|
|
593
617
|
|
|
594
618
|
def extra_context(self):
|
|
595
619
|
racks = self.queryset
|
|
@@ -641,14 +665,26 @@ class RackReservationUIViewSet(NautobotUIViewSet):
|
|
|
641
665
|
serializer_class = serializers.RackReservationSerializer
|
|
642
666
|
table_class = tables.RackReservationTable
|
|
643
667
|
queryset = RackReservation.objects.all()
|
|
668
|
+
breadcrumbs = Breadcrumbs(
|
|
669
|
+
items={
|
|
670
|
+
"detail": [
|
|
671
|
+
ModelBreadcrumbItem(),
|
|
672
|
+
InstanceBreadcrumbItem(instance=context_object_attr("rack")),
|
|
673
|
+
]
|
|
674
|
+
}
|
|
675
|
+
)
|
|
644
676
|
|
|
645
677
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
646
678
|
panels=(
|
|
647
|
-
object_detail.
|
|
679
|
+
object_detail.ObjectFieldsPanel(
|
|
648
680
|
section=SectionChoices.LEFT_HALF,
|
|
649
681
|
weight=100,
|
|
650
682
|
label="Rack",
|
|
651
|
-
|
|
683
|
+
fields=["rack__location", "rack__rack_group", "rack"],
|
|
684
|
+
key_transforms={
|
|
685
|
+
"rack__location": "Location",
|
|
686
|
+
"rack__rack_group": "Rack Group",
|
|
687
|
+
},
|
|
652
688
|
),
|
|
653
689
|
object_detail.ObjectFieldsPanel(
|
|
654
690
|
section=SectionChoices.LEFT_HALF,
|
|
@@ -664,23 +700,6 @@ class RackReservationUIViewSet(NautobotUIViewSet):
|
|
|
664
700
|
),
|
|
665
701
|
)
|
|
666
702
|
|
|
667
|
-
def get_extra_context(self, request, instance):
|
|
668
|
-
context = super().get_extra_context(request, instance)
|
|
669
|
-
if self.action == "retrieve":
|
|
670
|
-
context["rack_data"] = self.get_rack_context(instance)
|
|
671
|
-
return context
|
|
672
|
-
|
|
673
|
-
def get_rack_context(self, instance):
|
|
674
|
-
rack = getattr(instance, "rack", None)
|
|
675
|
-
if not rack:
|
|
676
|
-
return {}
|
|
677
|
-
|
|
678
|
-
return {
|
|
679
|
-
"location": rack.location,
|
|
680
|
-
"rack_group": rack.rack_group,
|
|
681
|
-
"rack": rack,
|
|
682
|
-
}
|
|
683
|
-
|
|
684
703
|
def get_object(self):
|
|
685
704
|
obj = super().get_object()
|
|
686
705
|
|
|
@@ -773,6 +792,15 @@ def bulk_footer_buttons(form_id: str, model):
|
|
|
773
792
|
]
|
|
774
793
|
|
|
775
794
|
|
|
795
|
+
def bulk_cable_termination_footer_buttons(form_id: str, model):
|
|
796
|
+
return [
|
|
797
|
+
BulkRenameButton(form_id=form_id, model=model),
|
|
798
|
+
BulkEditButton(form_id=form_id, model=model),
|
|
799
|
+
BulkDisconnectButton(form_id=form_id, model=model),
|
|
800
|
+
BulkDeleteButton(form_id=form_id, model=model),
|
|
801
|
+
]
|
|
802
|
+
|
|
803
|
+
|
|
776
804
|
# --- Tab Configuration ---
|
|
777
805
|
TAB_CONFIGS = [
|
|
778
806
|
(
|
|
@@ -888,6 +916,7 @@ def make_bulk_tab(weight, tab_name, label, url_name, related_attr, table_class,
|
|
|
888
916
|
label=label,
|
|
889
917
|
url_name=url_name,
|
|
890
918
|
related_object_attribute=related_attr,
|
|
919
|
+
hide_if_empty=True,
|
|
891
920
|
panels=(
|
|
892
921
|
object_detail.ObjectsTablePanel(
|
|
893
922
|
section=SectionChoices.FULL_WIDTH,
|
|
@@ -914,6 +943,14 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
914
943
|
serializer_class = serializers.DeviceTypeSerializer
|
|
915
944
|
table_class = tables.DeviceTypeTable
|
|
916
945
|
queryset = DeviceType.objects.select_related("manufacturer").prefetch_related("software_image_files")
|
|
946
|
+
breadcrumbs = Breadcrumbs(
|
|
947
|
+
items={
|
|
948
|
+
"detail": [
|
|
949
|
+
ModelBreadcrumbItem(),
|
|
950
|
+
InstanceParentBreadcrumbItem(parent_key="manufacturer", parent_lookup_key="name"),
|
|
951
|
+
]
|
|
952
|
+
}
|
|
953
|
+
)
|
|
917
954
|
|
|
918
955
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
919
956
|
panels=(
|
|
@@ -927,6 +964,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
927
964
|
weight=200,
|
|
928
965
|
table_class=tables.SoftwareImageFileTable,
|
|
929
966
|
table_filter="device_types",
|
|
967
|
+
order_by_fields=["image_file_name"],
|
|
930
968
|
select_related_fields=["software_version", "status"],
|
|
931
969
|
exclude_columns=["actions", "tags"],
|
|
932
970
|
related_field_name="device_types",
|
|
@@ -980,7 +1018,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
980
1018
|
@action(
|
|
981
1019
|
detail=True,
|
|
982
1020
|
methods=["get"],
|
|
983
|
-
url_path="
|
|
1021
|
+
url_path="front-ports",
|
|
984
1022
|
custom_view_base_action="view",
|
|
985
1023
|
custom_view_additional_permissions=["dcim.view_frontporttemplate"],
|
|
986
1024
|
)
|
|
@@ -990,7 +1028,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
990
1028
|
@action(
|
|
991
1029
|
detail=True,
|
|
992
1030
|
methods=["get"],
|
|
993
|
-
url_path="
|
|
1031
|
+
url_path="rear-ports",
|
|
994
1032
|
custom_view_base_action="view",
|
|
995
1033
|
custom_view_additional_permissions=["dcim.view_rearporttemplate"],
|
|
996
1034
|
)
|
|
@@ -1000,7 +1038,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
1000
1038
|
@action(
|
|
1001
1039
|
detail=True,
|
|
1002
1040
|
methods=["get"],
|
|
1003
|
-
url_path="
|
|
1041
|
+
url_path="console-ports",
|
|
1004
1042
|
custom_view_base_action="view",
|
|
1005
1043
|
custom_view_additional_permissions=["dcim.view_consoleporttemplate"],
|
|
1006
1044
|
)
|
|
@@ -1010,7 +1048,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
1010
1048
|
@action(
|
|
1011
1049
|
detail=True,
|
|
1012
1050
|
methods=["get"],
|
|
1013
|
-
url_path="
|
|
1051
|
+
url_path="console-server-ports",
|
|
1014
1052
|
custom_view_base_action="view",
|
|
1015
1053
|
custom_view_additional_permissions=["dcim.view_consoleserverporttemplate"],
|
|
1016
1054
|
)
|
|
@@ -1020,7 +1058,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
1020
1058
|
@action(
|
|
1021
1059
|
detail=True,
|
|
1022
1060
|
methods=["get"],
|
|
1023
|
-
url_path="
|
|
1061
|
+
url_path="power-ports",
|
|
1024
1062
|
custom_view_base_action="view",
|
|
1025
1063
|
custom_view_additional_permissions=["dcim.view_powerporttemplate"],
|
|
1026
1064
|
)
|
|
@@ -1030,7 +1068,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
1030
1068
|
@action(
|
|
1031
1069
|
detail=True,
|
|
1032
1070
|
methods=["get"],
|
|
1033
|
-
url_path="
|
|
1071
|
+
url_path="power-outlets",
|
|
1034
1072
|
custom_view_base_action="view",
|
|
1035
1073
|
custom_view_additional_permissions=["dcim.view_poweroutlettemplate"],
|
|
1036
1074
|
)
|
|
@@ -1040,7 +1078,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
1040
1078
|
@action(
|
|
1041
1079
|
detail=True,
|
|
1042
1080
|
methods=["get"],
|
|
1043
|
-
url_path="
|
|
1081
|
+
url_path="device-bays",
|
|
1044
1082
|
custom_view_base_action="view",
|
|
1045
1083
|
custom_view_additional_permissions=["dcim.view_devicebaytemplate"],
|
|
1046
1084
|
)
|
|
@@ -1050,7 +1088,7 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
|
|
|
1050
1088
|
@action(
|
|
1051
1089
|
detail=True,
|
|
1052
1090
|
methods=["get"],
|
|
1053
|
-
url_path="
|
|
1091
|
+
url_path="module-bays",
|
|
1054
1092
|
custom_view_base_action="view",
|
|
1055
1093
|
custom_view_additional_permissions=["dcim.view_modulebaytemplate"],
|
|
1056
1094
|
)
|
|
@@ -1121,6 +1159,14 @@ class ModuleTypeUIViewSet(
|
|
|
1121
1159
|
"front-ports": forms.FrontPortTemplateImportForm,
|
|
1122
1160
|
"module-bays": forms.ModuleBayTemplateImportForm,
|
|
1123
1161
|
}
|
|
1162
|
+
breadcrumbs = Breadcrumbs(
|
|
1163
|
+
items={
|
|
1164
|
+
"detail": [
|
|
1165
|
+
ModelBreadcrumbItem(),
|
|
1166
|
+
InstanceParentBreadcrumbItem(parent_key="manufacturer", parent_lookup_key="name"),
|
|
1167
|
+
]
|
|
1168
|
+
}
|
|
1169
|
+
)
|
|
1124
1170
|
|
|
1125
1171
|
def get_required_permission(self):
|
|
1126
1172
|
view_action = self.get_action()
|
|
@@ -1890,60 +1936,133 @@ class PlatformUIViewSet(NautobotUIViewSet):
|
|
|
1890
1936
|
#
|
|
1891
1937
|
|
|
1892
1938
|
|
|
1893
|
-
class
|
|
1939
|
+
class DeviceComponentPageMixin:
|
|
1940
|
+
"""
|
|
1941
|
+
This class hold the breadcrumbs paths for Device Components Pages like console ports.
|
|
1942
|
+
Depending on whether the component is associated with a device or a module, the appropriate breadcrumb path will be rendered.
|
|
1943
|
+
|
|
1944
|
+
For example:
|
|
1945
|
+
- Console Port assigned to the module: Modules / <Module name and link to details> / Console Ports (dcim/modules/<id>/console-ports/) / <Console Port name>
|
|
1946
|
+
- Console Port assigned to the device: Devices / <Device name and link to details> / Console Ports (dcim/devices/<id>/console-ports/) / <Console Port name>
|
|
1947
|
+
"""
|
|
1948
|
+
|
|
1894
1949
|
breadcrumbs = Breadcrumbs(
|
|
1895
1950
|
items={
|
|
1896
1951
|
"detail": [
|
|
1897
|
-
ModelBreadcrumbItem(model=Device),
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1952
|
+
ModelBreadcrumbItem(model=Device, should_render=lambda c: c["object"].device is not None),
|
|
1953
|
+
InstanceBreadcrumbItem(
|
|
1954
|
+
instance=lambda c: c["object"].device, should_render=lambda c: c["object"].device is not None
|
|
1955
|
+
),
|
|
1956
|
+
ViewNameBreadcrumbItem(
|
|
1957
|
+
view_name_key="device_breadcrumb_url",
|
|
1958
|
+
should_render=lambda c: c["object"].device is not None and c.get("device_breadcrumb_url"),
|
|
1959
|
+
reverse_kwargs=lambda c: {"pk": c["object"].device.pk},
|
|
1960
|
+
label=lambda c: c["object"]._meta.verbose_name_plural,
|
|
1901
1961
|
),
|
|
1962
|
+
ModelBreadcrumbItem(model=Module, should_render=lambda c: c["object"].device is None),
|
|
1902
1963
|
InstanceBreadcrumbItem(
|
|
1903
|
-
instance=lambda c: c["object"].
|
|
1904
|
-
should_render=lambda c: hasattr(c["object"], "parent_bay"),
|
|
1964
|
+
instance=lambda c: c["object"].module, should_render=lambda c: c["object"].device is None
|
|
1905
1965
|
),
|
|
1906
|
-
|
|
1907
|
-
|
|
1966
|
+
ViewNameBreadcrumbItem(
|
|
1967
|
+
view_name_key="module_breadcrumb_url",
|
|
1968
|
+
should_render=lambda c: c["object"].device is None and c.get("module_breadcrumb_url"),
|
|
1969
|
+
reverse_kwargs=lambda c: {"pk": c["object"].module.pk},
|
|
1970
|
+
label=lambda c: c["object"]._meta.verbose_name_plural,
|
|
1908
1971
|
),
|
|
1909
1972
|
]
|
|
1910
1973
|
}
|
|
1911
1974
|
)
|
|
1975
|
+
view_titles = Titles(
|
|
1976
|
+
titles={
|
|
1977
|
+
"detail": "{% if object.device %}{{ object.device }}{% else %}{{ object.module.display }}{% endif %} / {{ object }}"
|
|
1978
|
+
}
|
|
1979
|
+
)
|
|
1912
1980
|
|
|
1913
1981
|
|
|
1914
|
-
class
|
|
1982
|
+
class DeviceUIViewSet(NautobotUIViewSet):
|
|
1915
1983
|
queryset = Device.objects.select_related(
|
|
1916
1984
|
"device_type__manufacturer", # Needed for __str__() on device_type
|
|
1917
1985
|
)
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1986
|
+
filterset_class = filters.DeviceFilterSet
|
|
1987
|
+
filterset_form_class = forms.DeviceFilterForm
|
|
1988
|
+
table_class = tables.DeviceTable
|
|
1989
|
+
form_class = forms.DeviceForm
|
|
1990
|
+
bulk_update_form_class = forms.DeviceBulkEditForm
|
|
1991
|
+
serializer_class = serializers.DeviceSerializer
|
|
1922
1992
|
|
|
1993
|
+
breadcrumbs = Breadcrumbs(
|
|
1994
|
+
items={
|
|
1995
|
+
"detail": [
|
|
1996
|
+
ModelBreadcrumbItem(model=Device),
|
|
1997
|
+
InstanceParentBreadcrumbItem(parent_key="location"),
|
|
1998
|
+
InstanceBreadcrumbItem(
|
|
1999
|
+
instance=lambda c: c["object"].parent_bay.device,
|
|
2000
|
+
should_render=lambda c: hasattr(c["object"], "parent_bay"),
|
|
2001
|
+
),
|
|
2002
|
+
BaseBreadcrumbItem(
|
|
2003
|
+
label=lambda c: c["object"].parent_bay, should_render=lambda c: hasattr(c["object"], "parent_bay")
|
|
2004
|
+
),
|
|
2005
|
+
]
|
|
2006
|
+
}
|
|
2007
|
+
)
|
|
1923
2008
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
"
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2009
|
+
def get_queryset(self):
|
|
2010
|
+
queryset = super().get_queryset()
|
|
2011
|
+
if self.detail: # TODO: change to self.action == "retrieve" as a part of addressing NAUTOBOT-1051
|
|
2012
|
+
queryset = queryset.select_related(
|
|
2013
|
+
"cluster__cluster_group",
|
|
2014
|
+
"controller_managed_device_group__controller",
|
|
2015
|
+
"device_redundancy_group",
|
|
2016
|
+
"device_type__device_family",
|
|
2017
|
+
"location",
|
|
2018
|
+
"platform",
|
|
2019
|
+
"primary_ip4",
|
|
2020
|
+
"primary_ip6",
|
|
2021
|
+
"rack__rack_group",
|
|
2022
|
+
"role",
|
|
2023
|
+
"secrets_group",
|
|
2024
|
+
"software_version",
|
|
2025
|
+
"status",
|
|
2026
|
+
"tenant__tenant_group",
|
|
2027
|
+
"virtual_chassis",
|
|
2028
|
+
).prefetch_related("images", "software_image_files")
|
|
2029
|
+
if self.action == "config_context":
|
|
2030
|
+
queryset = queryset.annotate_config_context_data()
|
|
2031
|
+
return queryset
|
|
1941
2032
|
|
|
1942
2033
|
class DeviceDetailContent(object_detail.ObjectDetailContent):
|
|
1943
2034
|
"""
|
|
1944
2035
|
Override base ObjectDetailContent to render dynamic-groups table as a separate view/tab instead of inline.
|
|
1945
2036
|
"""
|
|
1946
2037
|
|
|
2038
|
+
class DeviceDynamicGroupsTextPanel(object_detail.BaseTextPanel):
|
|
2039
|
+
"""Panel displaying a note about caching of dynamic groups."""
|
|
2040
|
+
|
|
2041
|
+
def __init__(
|
|
2042
|
+
self,
|
|
2043
|
+
*,
|
|
2044
|
+
weight,
|
|
2045
|
+
render_as=object_detail.BaseTextPanel.RenderOptions.MARKDOWN,
|
|
2046
|
+
label="Dynamic Group caching",
|
|
2047
|
+
**kwargs,
|
|
2048
|
+
):
|
|
2049
|
+
super().__init__(weight=weight, render_as=render_as, label=label, **kwargs)
|
|
2050
|
+
|
|
2051
|
+
def get_value(self, context):
|
|
2052
|
+
dg_list_url = reverse("extras:dynamicgroup_list")
|
|
2053
|
+
job_run_url = reverse(
|
|
2054
|
+
"extras:job_run_by_class_path",
|
|
2055
|
+
kwargs={"class_path": "nautobot.core.jobs.groups.RefreshDynamicGroupCaches"},
|
|
2056
|
+
)
|
|
2057
|
+
return (
|
|
2058
|
+
"Dynamic group membership is cached for performance reasons, "
|
|
2059
|
+
"therefore this page may not always be up-to-date.\n\n"
|
|
2060
|
+
"You can refresh the membership of any specific group by viewing it from the list below or from the "
|
|
2061
|
+
f"[Dynamic Groups list view]({dg_list_url}).\n\n"
|
|
2062
|
+
"You can also refresh the membership of **all** groups by running the "
|
|
2063
|
+
f"[Refresh Dynamic Group Caches job]({job_run_url})."
|
|
2064
|
+
)
|
|
2065
|
+
|
|
1947
2066
|
def __init__(self, **kwargs):
|
|
1948
2067
|
super().__init__(**kwargs)
|
|
1949
2068
|
# Remove inline tab definition
|
|
@@ -1959,8 +2078,9 @@ class DeviceView(generic.ObjectView):
|
|
|
1959
2078
|
url_name="dcim:device_dynamicgroups",
|
|
1960
2079
|
related_object_attribute="dynamic_groups",
|
|
1961
2080
|
panels=(
|
|
2081
|
+
self.DeviceDynamicGroupsTextPanel(weight=100),
|
|
1962
2082
|
object_detail.ObjectsTablePanel(
|
|
1963
|
-
weight=
|
|
2083
|
+
weight=200,
|
|
1964
2084
|
table_class=DynamicGroupTable,
|
|
1965
2085
|
table_attribute="dynamic_groups",
|
|
1966
2086
|
exclude_columns=["content_type"],
|
|
@@ -1972,6 +2092,191 @@ class DeviceView(generic.ObjectView):
|
|
|
1972
2092
|
)
|
|
1973
2093
|
)
|
|
1974
2094
|
|
|
2095
|
+
class DeviceFieldsPanel(object_detail.ObjectFieldsPanel):
|
|
2096
|
+
"""
|
|
2097
|
+
ObjectFieldsPanel with context-aware rendering of `position`, `device_redundancy_group`, and `software_version`.
|
|
2098
|
+
"""
|
|
2099
|
+
|
|
2100
|
+
def render_value(self, key, value, context):
|
|
2101
|
+
if key == "position":
|
|
2102
|
+
instance = get_obj_from_context(context, self.context_object_key)
|
|
2103
|
+
try:
|
|
2104
|
+
if instance.parent_bay is not None:
|
|
2105
|
+
parent = instance.parent_bay.device
|
|
2106
|
+
display = format_html(
|
|
2107
|
+
"{} / {}",
|
|
2108
|
+
helpers.hyperlinked_object(parent),
|
|
2109
|
+
helpers.hyperlinked_object(instance.parent_bay),
|
|
2110
|
+
)
|
|
2111
|
+
if parent.position is not None:
|
|
2112
|
+
display += format_html(" (U{} / {})", parent.position, parent.get_face_display())
|
|
2113
|
+
return display
|
|
2114
|
+
except DeviceBay.DoesNotExist:
|
|
2115
|
+
pass
|
|
2116
|
+
if instance.rack is not None and value is not None:
|
|
2117
|
+
return format_html("U{} / {}", value, instance.get_face_display())
|
|
2118
|
+
if instance.rack is not None and instance.device_type.u_height:
|
|
2119
|
+
return mark_safe('<span class="label label-warning">Not racked</span>')
|
|
2120
|
+
return helpers.HTML_NONE
|
|
2121
|
+
if key == "device_redundancy_group" and value is not None:
|
|
2122
|
+
instance = get_obj_from_context(context, self.context_object_key)
|
|
2123
|
+
return format_html(
|
|
2124
|
+
'{} <span class="badge badge-default">Priority: {}</span>',
|
|
2125
|
+
helpers.hyperlinked_object(value),
|
|
2126
|
+
instance.device_redundancy_group_priority,
|
|
2127
|
+
)
|
|
2128
|
+
if key == "software_version":
|
|
2129
|
+
instance = get_obj_from_context(context, self.context_object_key)
|
|
2130
|
+
return render_software_version_and_image_files(instance, value, context)
|
|
2131
|
+
|
|
2132
|
+
return super().render_value(key, value, context)
|
|
2133
|
+
|
|
2134
|
+
class DeviceVirtualChassisMembersTablePanel(object_detail.ObjectsTablePanel):
|
|
2135
|
+
"""ObjectsTablePanel that only renders if the device belongs to a virtual-chassis."""
|
|
2136
|
+
|
|
2137
|
+
def should_render(self, context):
|
|
2138
|
+
obj = get_obj_from_context(context)
|
|
2139
|
+
return obj.virtual_chassis is not None
|
|
2140
|
+
|
|
2141
|
+
def get_extra_context(self, context):
|
|
2142
|
+
extra_context = super().get_extra_context(context)
|
|
2143
|
+
obj = get_obj_from_context(context)
|
|
2144
|
+
extra_context["virtual_chassis"] = obj.virtual_chassis
|
|
2145
|
+
extra_context["body_content_table_list_url"] = (
|
|
2146
|
+
reverse("dcim:device_list") + "?virtual_chassis=" + str(obj.virtual_chassis.pk)
|
|
2147
|
+
)
|
|
2148
|
+
return extra_context
|
|
2149
|
+
|
|
2150
|
+
class DevicePowerUtilizationPanel(object_detail.Panel):
|
|
2151
|
+
"""Panel showing a table of PDU calculated power utilization per power-port on the device."""
|
|
2152
|
+
|
|
2153
|
+
def should_render(self, context):
|
|
2154
|
+
"""Only render if the device is a PDU, i.e. has both power-ports and power-outlets."""
|
|
2155
|
+
instance = get_obj_from_context(context)
|
|
2156
|
+
return instance.all_power_ports.exists() and instance.all_power_outlets.exists()
|
|
2157
|
+
|
|
2158
|
+
def render_body_content(self, context):
|
|
2159
|
+
"""Render a table with one row per power-port and additional rows per leg for three-phase power."""
|
|
2160
|
+
instance = get_obj_from_context(context)
|
|
2161
|
+
header = mark_safe(
|
|
2162
|
+
"<tr><th>Input</th><th>Outlets</th><th>Allocated</th><th>Available</th><th>Utilization</th></tr>"
|
|
2163
|
+
)
|
|
2164
|
+
body = mark_safe("")
|
|
2165
|
+
for powerport in instance.all_power_ports.all():
|
|
2166
|
+
utilization = powerport.get_power_draw()
|
|
2167
|
+
# Table row for each power-port
|
|
2168
|
+
powerfeed = powerport.connected_endpoint
|
|
2169
|
+
if powerfeed is not None and powerfeed.available_power:
|
|
2170
|
+
available_power = powerfeed.available_power
|
|
2171
|
+
utilization_data = Context(
|
|
2172
|
+
helpers.utilization_graph_raw_data(utilization["allocated"], powerfeed.available_power)
|
|
2173
|
+
)
|
|
2174
|
+
utilization_graph = object_detail.render_component_template(
|
|
2175
|
+
"utilities/templatetags/utilization_graph.html", utilization_data
|
|
2176
|
+
)
|
|
2177
|
+
else:
|
|
2178
|
+
available_power = helpers.HTML_NONE
|
|
2179
|
+
utilization_graph = helpers.HTML_NONE
|
|
2180
|
+
body += format_html(
|
|
2181
|
+
"<tr><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>",
|
|
2182
|
+
helpers.hyperlinked_object(powerport),
|
|
2183
|
+
utilization["outlet_count"],
|
|
2184
|
+
utilization["allocated"],
|
|
2185
|
+
available_power,
|
|
2186
|
+
utilization_graph,
|
|
2187
|
+
)
|
|
2188
|
+
|
|
2189
|
+
# Indented table row for each leg of a three-phase power-port.
|
|
2190
|
+
for leg in utilization["legs"]:
|
|
2191
|
+
if powerfeed is not None and powerfeed.available_power:
|
|
2192
|
+
available_power = powerfeed.available_power / 3
|
|
2193
|
+
utilization_data = Context(
|
|
2194
|
+
helpers.utilization_graph_raw_data(leg["allocated"], powerfeed.available_power / 3)
|
|
2195
|
+
)
|
|
2196
|
+
utilization_graph = object_detail.render_component_template(
|
|
2197
|
+
"utilities/templatetags/utilization_graph.html", utilization_data
|
|
2198
|
+
)
|
|
2199
|
+
else:
|
|
2200
|
+
available_power = helpers.HTML_NONE
|
|
2201
|
+
utilization_graph = helpers.HTML_NONE
|
|
2202
|
+
body += format_html(
|
|
2203
|
+
"""<tr><td style="padding-left: 20px">{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>""",
|
|
2204
|
+
f"Leg {leg['name']}",
|
|
2205
|
+
leg["outlet_count"],
|
|
2206
|
+
leg["allocated"],
|
|
2207
|
+
available_power,
|
|
2208
|
+
utilization_graph,
|
|
2209
|
+
)
|
|
2210
|
+
return header + body
|
|
2211
|
+
|
|
2212
|
+
class DeviceImageAttachmentsTablePanel(object_detail.ObjectsTablePanel):
|
|
2213
|
+
"""
|
|
2214
|
+
ObjectsTablePanel with a custom _get_table_add_url() implementation.
|
|
2215
|
+
|
|
2216
|
+
Needed because the URL is `/dcim/devices/<pk>/images/add/`, not `extras/image-attachments/add?device=<pk>`.
|
|
2217
|
+
"""
|
|
2218
|
+
|
|
2219
|
+
def _get_table_add_url(self, context):
|
|
2220
|
+
obj = get_obj_from_context(context)
|
|
2221
|
+
request = context["request"]
|
|
2222
|
+
return_url = context.get("return_url", obj.get_absolute_url())
|
|
2223
|
+
|
|
2224
|
+
if not request.user.has_perms(["extras.add_imageattachment"]):
|
|
2225
|
+
return None
|
|
2226
|
+
return reverse("dcim:device_add_image", kwargs={"object_id": obj.pk}) + f"?return_url={return_url}"
|
|
2227
|
+
|
|
2228
|
+
class DeviceModuleBaysTab(object_detail.DistinctViewTab):
|
|
2229
|
+
"""DistinctViewTab for device module-bays; shows both the module and module-bay count as a badge."""
|
|
2230
|
+
|
|
2231
|
+
def render_label(self, context):
|
|
2232
|
+
obj = get_obj_from_context(context)
|
|
2233
|
+
module_count = obj.module_bays.filter(installed_module__isnull=False).count()
|
|
2234
|
+
return format_html(
|
|
2235
|
+
"{} {}",
|
|
2236
|
+
self.label,
|
|
2237
|
+
render_to_string(
|
|
2238
|
+
"utilities/templatetags/badge.html", helpers.badge(f"{module_count}/{self.related_object_count}")
|
|
2239
|
+
),
|
|
2240
|
+
)
|
|
2241
|
+
|
|
2242
|
+
class DeviceInterfacesTablePanel(object_detail.ObjectsTablePanel):
|
|
2243
|
+
"""ObjectsTablePanel for device interfaces; shows the "Device" column if the device is a VirtualChassis master."""
|
|
2244
|
+
|
|
2245
|
+
def get_extra_context(self, context):
|
|
2246
|
+
extra_context = super().get_extra_context(context)
|
|
2247
|
+
obj = get_obj_from_context(context)
|
|
2248
|
+
if VirtualChassis.objects.filter(master=obj).exists():
|
|
2249
|
+
extra_context["body_content_table"].columns.show("device")
|
|
2250
|
+
return extra_context
|
|
2251
|
+
|
|
2252
|
+
class DeviceWirelessTab(object_detail.DistinctViewTab):
|
|
2253
|
+
"""
|
|
2254
|
+
DistinctViewTab that only renders if the device belongs to a wireless-capable controller_managed_device_group.
|
|
2255
|
+
"""
|
|
2256
|
+
|
|
2257
|
+
def should_render(self, context):
|
|
2258
|
+
if not super().should_render(context):
|
|
2259
|
+
return False
|
|
2260
|
+
obj = get_obj_from_context(context)
|
|
2261
|
+
return (
|
|
2262
|
+
obj.controller_managed_device_group is not None
|
|
2263
|
+
and isinstance(obj.controller_managed_device_group.capabilities, list) # it's potentially None
|
|
2264
|
+
and "wireless" in obj.controller_managed_device_group.capabilities
|
|
2265
|
+
)
|
|
2266
|
+
|
|
2267
|
+
class DeviceNAPALMTab(object_detail.DistinctViewTab):
|
|
2268
|
+
"""DistinctViewTab for device NAPALM getters; disables the tab if the device/platform isn't set appropriately."""
|
|
2269
|
+
|
|
2270
|
+
def render_label_wrapper(self, context):
|
|
2271
|
+
obj = get_obj_from_context(context)
|
|
2272
|
+
if obj.platform is None:
|
|
2273
|
+
with context.update({"disabled_message": "No platform assigned to this device"}):
|
|
2274
|
+
return super().render_label_wrapper(context)
|
|
2275
|
+
if not obj.platform.napalm_driver:
|
|
2276
|
+
with context.update({"disabled_message": "No NAPALM driver assigned for this platform"}):
|
|
2277
|
+
return super().render_label_wrapper(context)
|
|
2278
|
+
return super().render_label_wrapper(context)
|
|
2279
|
+
|
|
1975
2280
|
object_detail_content = DeviceDetailContent(
|
|
1976
2281
|
extra_buttons=(
|
|
1977
2282
|
object_detail.DropdownButton(
|
|
@@ -2055,486 +2360,665 @@ class DeviceView(generic.ObjectView):
|
|
|
2055
2360
|
),
|
|
2056
2361
|
),
|
|
2057
2362
|
),
|
|
2058
|
-
panels=(
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
.
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
.
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2363
|
+
panels=(
|
|
2364
|
+
DeviceFieldsPanel(
|
|
2365
|
+
weight=100,
|
|
2366
|
+
section=SectionChoices.LEFT_HALF,
|
|
2367
|
+
fields=[
|
|
2368
|
+
"location",
|
|
2369
|
+
"rack",
|
|
2370
|
+
"position",
|
|
2371
|
+
"tenant",
|
|
2372
|
+
"device_type__device_family",
|
|
2373
|
+
"device_type",
|
|
2374
|
+
"serial",
|
|
2375
|
+
"asset_tag",
|
|
2376
|
+
],
|
|
2377
|
+
key_transforms={"device_type__device_family": "Device Family"},
|
|
2378
|
+
value_transforms={
|
|
2379
|
+
"device_type": [lambda v: format_html("{} ({}U)", helpers.hyperlinked_object(v), v.u_height)],
|
|
2380
|
+
},
|
|
2381
|
+
),
|
|
2382
|
+
DeviceVirtualChassisMembersTablePanel(
|
|
2383
|
+
weight=110,
|
|
2384
|
+
section=SectionChoices.LEFT_HALF,
|
|
2385
|
+
context_table_key="vc_members_table",
|
|
2386
|
+
table_title="Virtual Chassis",
|
|
2387
|
+
related_field_name="vc_master",
|
|
2388
|
+
show_table_config_button=False,
|
|
2389
|
+
add_button_route=None,
|
|
2390
|
+
footer_buttons=[
|
|
2391
|
+
object_detail.Button(
|
|
2392
|
+
weight=100,
|
|
2393
|
+
label="View Virtual Chassis",
|
|
2394
|
+
icon="mdi-arrow-right-bold",
|
|
2395
|
+
size="xs",
|
|
2396
|
+
link_name="dcim:virtualchassis",
|
|
2397
|
+
context_object_key="virtual_chassis",
|
|
2398
|
+
),
|
|
2399
|
+
],
|
|
2400
|
+
),
|
|
2401
|
+
DeviceFieldsPanel(
|
|
2402
|
+
weight=120,
|
|
2403
|
+
section=SectionChoices.LEFT_HALF,
|
|
2404
|
+
label="Management",
|
|
2405
|
+
fields=[
|
|
2406
|
+
"role",
|
|
2407
|
+
"platform",
|
|
2408
|
+
"status",
|
|
2409
|
+
"primary_ip4",
|
|
2410
|
+
"primary_ip6",
|
|
2411
|
+
"secrets_group",
|
|
2412
|
+
"device_redundancy_group",
|
|
2413
|
+
"controller_managed_device_group",
|
|
2414
|
+
"controller_managed_device_group__controller",
|
|
2415
|
+
"cluster",
|
|
2416
|
+
"software_version",
|
|
2417
|
+
],
|
|
2418
|
+
key_transforms={"controller_managed_device_group__controller": "Managed By Controller"},
|
|
2419
|
+
value_transforms={
|
|
2420
|
+
"primary_ip4": [render_ip_with_nat],
|
|
2421
|
+
"primary_ip6": [render_ip_with_nat],
|
|
2422
|
+
},
|
|
2423
|
+
hide_if_unset=["controller_managed_device_group__controller", "cluster"],
|
|
2424
|
+
),
|
|
2425
|
+
DevicePowerUtilizationPanel(
|
|
2426
|
+
weight=100,
|
|
2427
|
+
section=SectionChoices.RIGHT_HALF,
|
|
2428
|
+
label="Power Utilization",
|
|
2429
|
+
body_wrapper_template_path="components/panel/body_wrapper_generic_table.html",
|
|
2430
|
+
),
|
|
2431
|
+
object_detail.ObjectsTablePanel(
|
|
2432
|
+
weight=200,
|
|
2433
|
+
section=SectionChoices.RIGHT_HALF,
|
|
2434
|
+
table_title="Assigned VRFs",
|
|
2435
|
+
table_class=VRFDeviceAssignmentTable,
|
|
2436
|
+
table_filter="device",
|
|
2437
|
+
exclude_columns=["related_object_type", "related_object_name"],
|
|
2438
|
+
show_table_config_button=False,
|
|
2439
|
+
),
|
|
2440
|
+
object_detail.ObjectsTablePanel(
|
|
2441
|
+
weight=300,
|
|
2442
|
+
section=SectionChoices.RIGHT_HALF,
|
|
2443
|
+
table_title="Services",
|
|
2444
|
+
table_class=ServiceTable,
|
|
2445
|
+
table_filter="device",
|
|
2446
|
+
exclude_columns=["parent"],
|
|
2447
|
+
include_columns=["ip_addresses"],
|
|
2448
|
+
show_table_config_button=False,
|
|
2449
|
+
),
|
|
2450
|
+
DeviceImageAttachmentsTablePanel(
|
|
2451
|
+
weight=400,
|
|
2452
|
+
section=SectionChoices.RIGHT_HALF,
|
|
2453
|
+
table_title="Images",
|
|
2454
|
+
table_class=ImageAttachmentTable,
|
|
2455
|
+
table_attribute="images",
|
|
2456
|
+
related_field_name="device",
|
|
2457
|
+
show_table_config_button=False,
|
|
2458
|
+
),
|
|
2459
|
+
object_detail.ObjectsTablePanel(
|
|
2460
|
+
weight=100,
|
|
2461
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2462
|
+
table_title="Virtual Device Contexts",
|
|
2463
|
+
table_class=tables.VirtualDeviceContextTable,
|
|
2464
|
+
table_filter="device",
|
|
2465
|
+
exclude_columns=["device"],
|
|
2466
|
+
show_table_config_button=False,
|
|
2467
|
+
),
|
|
2468
|
+
),
|
|
2469
|
+
extra_tabs=(
|
|
2470
|
+
DeviceModuleBaysTab(
|
|
2471
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 100,
|
|
2472
|
+
tab_id="module_bays",
|
|
2473
|
+
label="Module Bays",
|
|
2474
|
+
url_name="dcim:device_modulebays",
|
|
2475
|
+
related_object_attribute="module_bays",
|
|
2476
|
+
hide_if_empty=True,
|
|
2477
|
+
panels=(
|
|
2478
|
+
object_detail.ObjectsTablePanel(
|
|
2479
|
+
weight=100,
|
|
2480
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2481
|
+
table_title="Module Bays",
|
|
2482
|
+
table_class=tables.DeviceModuleBayTable,
|
|
2483
|
+
prefetch_related_fields=["installed_module", "installed_module__status"],
|
|
2484
|
+
table_filter="parent_device", # TODO: is this right or should we use table_attribute=module_bays?
|
|
2485
|
+
tab_id="module_bays",
|
|
2486
|
+
enable_bulk_actions=True,
|
|
2487
|
+
form_id="module-bays-form",
|
|
2488
|
+
footer_buttons=bulk_footer_buttons(form_id="module-bays-form", model=ModuleBay),
|
|
2489
|
+
include_paginator=True,
|
|
2490
|
+
),
|
|
2491
|
+
),
|
|
2492
|
+
),
|
|
2493
|
+
object_detail.DistinctViewTab(
|
|
2494
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 200,
|
|
2495
|
+
tab_id="interfaces",
|
|
2496
|
+
label="Interfaces",
|
|
2497
|
+
url_name="dcim:device_interfaces",
|
|
2498
|
+
related_object_attribute="vc_interfaces",
|
|
2499
|
+
hide_if_empty=True,
|
|
2500
|
+
panels=(
|
|
2501
|
+
DeviceInterfacesTablePanel(
|
|
2502
|
+
# TODO: .prefetch_related(ip_addresses.restrict, member_interfaces.restrict)
|
|
2503
|
+
weight=100,
|
|
2504
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2505
|
+
table_title="Interfaces",
|
|
2506
|
+
table_class=tables.DeviceModuleInterfaceTable,
|
|
2507
|
+
table_attribute="vc_interfaces",
|
|
2508
|
+
order_by_fields=["_name"],
|
|
2509
|
+
prefetch_related_fields=["_path__destination"],
|
|
2510
|
+
select_related_fields=["cable", "lag"],
|
|
2511
|
+
related_field_name="device",
|
|
2512
|
+
tab_id="interfaces",
|
|
2513
|
+
enable_bulk_actions=True,
|
|
2514
|
+
form_id="interfaces-form",
|
|
2515
|
+
footer_buttons=bulk_cable_termination_footer_buttons(
|
|
2516
|
+
form_id="interfaces-form", model=Interface
|
|
2517
|
+
),
|
|
2518
|
+
include_paginator=True,
|
|
2519
|
+
header_extra_content_template_path="dcim/inc/device_interface_filter.html",
|
|
2520
|
+
),
|
|
2521
|
+
),
|
|
2522
|
+
),
|
|
2523
|
+
object_detail.DistinctViewTab(
|
|
2524
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 300,
|
|
2525
|
+
tab_id="front_ports",
|
|
2526
|
+
label="Front Ports",
|
|
2527
|
+
url_name="dcim:device_frontports",
|
|
2528
|
+
related_object_attribute="all_front_ports",
|
|
2529
|
+
hide_if_empty=True,
|
|
2530
|
+
panels=(
|
|
2531
|
+
object_detail.ObjectsTablePanel(
|
|
2532
|
+
weight=100,
|
|
2533
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2534
|
+
table_title="Front Ports",
|
|
2535
|
+
table_class=tables.DeviceModuleFrontPortTable,
|
|
2536
|
+
table_attribute="all_front_ports",
|
|
2537
|
+
select_related_fields=["cable", "rear_port"],
|
|
2538
|
+
related_field_name="device",
|
|
2539
|
+
tab_id="front_ports",
|
|
2540
|
+
enable_bulk_actions=True,
|
|
2541
|
+
form_id="front-ports-form",
|
|
2542
|
+
footer_buttons=bulk_cable_termination_footer_buttons(
|
|
2543
|
+
form_id="front-ports-form", model=FrontPort
|
|
2544
|
+
),
|
|
2545
|
+
include_paginator=True,
|
|
2546
|
+
),
|
|
2547
|
+
),
|
|
2548
|
+
),
|
|
2549
|
+
object_detail.DistinctViewTab(
|
|
2550
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 400,
|
|
2551
|
+
tab_id="rear_ports",
|
|
2552
|
+
label="Rear Ports",
|
|
2553
|
+
url_name="dcim:device_rearports",
|
|
2554
|
+
related_object_attribute="all_rear_ports",
|
|
2555
|
+
hide_if_empty=True,
|
|
2556
|
+
panels=(
|
|
2557
|
+
object_detail.ObjectsTablePanel(
|
|
2558
|
+
weight=100,
|
|
2559
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2560
|
+
table_title="Rear Ports",
|
|
2561
|
+
table_class=tables.DeviceModuleRearPortTable,
|
|
2562
|
+
table_attribute="all_rear_ports",
|
|
2563
|
+
select_related_fields=["cable"],
|
|
2564
|
+
related_field_name="device",
|
|
2565
|
+
tab_id="rear_ports",
|
|
2566
|
+
enable_bulk_actions=True,
|
|
2567
|
+
form_id="rear-ports-form",
|
|
2568
|
+
footer_buttons=bulk_cable_termination_footer_buttons(form_id="rear-ports-form", model=RearPort),
|
|
2569
|
+
include_paginator=True,
|
|
2570
|
+
),
|
|
2571
|
+
),
|
|
2572
|
+
),
|
|
2573
|
+
object_detail.DistinctViewTab(
|
|
2574
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 500,
|
|
2575
|
+
tab_id="console_ports",
|
|
2576
|
+
label="Console Ports",
|
|
2577
|
+
url_name="dcim:device_consoleports",
|
|
2578
|
+
related_object_attribute="all_console_ports",
|
|
2579
|
+
hide_if_empty=True,
|
|
2580
|
+
panels=(
|
|
2581
|
+
object_detail.ObjectsTablePanel(
|
|
2582
|
+
weight=100,
|
|
2583
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2584
|
+
table_title="Console Ports",
|
|
2585
|
+
table_class=tables.DeviceModuleConsolePortTable,
|
|
2586
|
+
table_attribute="all_console_ports",
|
|
2587
|
+
select_related_fields=["cable"],
|
|
2588
|
+
prefetch_related_fields=["_path__destination"],
|
|
2589
|
+
related_field_name="device",
|
|
2590
|
+
tab_id="console_ports",
|
|
2591
|
+
enable_bulk_actions=True,
|
|
2592
|
+
form_id="console-ports-form",
|
|
2593
|
+
footer_buttons=bulk_cable_termination_footer_buttons(
|
|
2594
|
+
form_id="console-ports-form", model=ConsolePort
|
|
2595
|
+
),
|
|
2596
|
+
include_paginator=True,
|
|
2597
|
+
),
|
|
2598
|
+
),
|
|
2599
|
+
),
|
|
2600
|
+
object_detail.DistinctViewTab(
|
|
2601
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 600,
|
|
2602
|
+
tab_id="console_server_ports",
|
|
2603
|
+
label="Console Server Ports",
|
|
2604
|
+
url_name="dcim:device_consoleserverports",
|
|
2605
|
+
related_object_attribute="all_console_server_ports",
|
|
2606
|
+
hide_if_empty=True,
|
|
2607
|
+
panels=(
|
|
2608
|
+
object_detail.ObjectsTablePanel(
|
|
2609
|
+
weight=100,
|
|
2610
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2611
|
+
table_title="Console Server Ports",
|
|
2612
|
+
table_class=tables.DeviceModuleConsoleServerPortTable,
|
|
2613
|
+
table_attribute="all_console_server_ports",
|
|
2614
|
+
select_related_fields=["cable"],
|
|
2615
|
+
prefetch_related_fields=["_path__destination"],
|
|
2616
|
+
related_field_name="device",
|
|
2617
|
+
tab_id="console_server_ports",
|
|
2618
|
+
enable_bulk_actions=True,
|
|
2619
|
+
form_id="console-server-ports-form",
|
|
2620
|
+
footer_buttons=bulk_cable_termination_footer_buttons(
|
|
2621
|
+
form_id="console-server-ports-form", model=ConsoleServerPort
|
|
2622
|
+
),
|
|
2623
|
+
include_paginator=True,
|
|
2624
|
+
),
|
|
2625
|
+
),
|
|
2626
|
+
),
|
|
2627
|
+
object_detail.DistinctViewTab(
|
|
2628
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 700,
|
|
2629
|
+
tab_id="power_ports",
|
|
2630
|
+
label="Power Ports",
|
|
2631
|
+
url_name="dcim:device_powerports",
|
|
2632
|
+
related_object_attribute="all_power_ports",
|
|
2633
|
+
hide_if_empty=True,
|
|
2634
|
+
panels=(
|
|
2635
|
+
object_detail.ObjectsTablePanel(
|
|
2636
|
+
weight=100,
|
|
2637
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2638
|
+
table_title="Power Ports",
|
|
2639
|
+
table_class=tables.DeviceModulePowerPortTable,
|
|
2640
|
+
table_attribute="all_power_ports",
|
|
2641
|
+
select_related_fields=["cable"],
|
|
2642
|
+
prefetch_related_fields=["_path__destination"],
|
|
2643
|
+
related_field_name="device",
|
|
2644
|
+
tab_id="power_ports",
|
|
2645
|
+
enable_bulk_actions=True,
|
|
2646
|
+
form_id="power-ports-form",
|
|
2647
|
+
footer_buttons=bulk_cable_termination_footer_buttons(
|
|
2648
|
+
form_id="power-ports-form", model=PowerPort
|
|
2649
|
+
),
|
|
2650
|
+
include_paginator=True,
|
|
2651
|
+
),
|
|
2652
|
+
),
|
|
2653
|
+
),
|
|
2654
|
+
object_detail.DistinctViewTab(
|
|
2655
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 800,
|
|
2656
|
+
tab_id="power_outlets",
|
|
2657
|
+
label="Power Outlets",
|
|
2658
|
+
url_name="dcim:device_poweroutlets",
|
|
2659
|
+
related_object_attribute="all_power_outlets",
|
|
2660
|
+
hide_if_empty=True,
|
|
2661
|
+
panels=(
|
|
2662
|
+
object_detail.ObjectsTablePanel(
|
|
2663
|
+
weight=100,
|
|
2664
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2665
|
+
table_title="Power Outlets",
|
|
2666
|
+
table_class=tables.DeviceModulePowerOutletTable,
|
|
2667
|
+
table_attribute="all_power_outlets",
|
|
2668
|
+
select_related_fields=["cable", "power_port"],
|
|
2669
|
+
prefetch_related_fields=["_path__destination"],
|
|
2670
|
+
related_field_name="device",
|
|
2671
|
+
tab_id="power_outlets",
|
|
2672
|
+
enable_bulk_actions=True,
|
|
2673
|
+
form_id="power-outlets-form",
|
|
2674
|
+
footer_buttons=bulk_cable_termination_footer_buttons(
|
|
2675
|
+
form_id="power-outlets-form", model=PowerOutlet
|
|
2676
|
+
),
|
|
2677
|
+
include_paginator=True,
|
|
2678
|
+
),
|
|
2679
|
+
),
|
|
2680
|
+
),
|
|
2681
|
+
object_detail.DistinctViewTab(
|
|
2682
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 900,
|
|
2683
|
+
tab_id="device_bays",
|
|
2684
|
+
label="Device Bays",
|
|
2685
|
+
url_name="dcim:device_devicebays",
|
|
2686
|
+
related_object_attribute="device_bays",
|
|
2687
|
+
hide_if_empty=True,
|
|
2688
|
+
panels=(
|
|
2689
|
+
object_detail.ObjectsTablePanel(
|
|
2690
|
+
weight=100,
|
|
2691
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2692
|
+
table_title="Device Bays",
|
|
2693
|
+
table_class=tables.DeviceDeviceBayTable,
|
|
2694
|
+
select_related_fields=["installed_device__device_type__manufacturer"],
|
|
2695
|
+
table_filter="device",
|
|
2696
|
+
tab_id="device_bays",
|
|
2697
|
+
enable_bulk_actions=True,
|
|
2698
|
+
form_id="device-bays-form",
|
|
2699
|
+
footer_buttons=bulk_footer_buttons(form_id="device-bays-form", model=DeviceBay),
|
|
2700
|
+
include_paginator=True,
|
|
2701
|
+
),
|
|
2702
|
+
),
|
|
2703
|
+
),
|
|
2704
|
+
object_detail.DistinctViewTab(
|
|
2705
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 1000,
|
|
2706
|
+
tab_id="inventory",
|
|
2707
|
+
label="Inventory",
|
|
2708
|
+
url_name="dcim:device_inventory",
|
|
2709
|
+
related_object_attribute="inventory_items",
|
|
2710
|
+
hide_if_empty=True,
|
|
2711
|
+
panels=(
|
|
2712
|
+
object_detail.ObjectsTablePanel(
|
|
2713
|
+
weight=100,
|
|
2714
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2715
|
+
table_title="Inventory Items",
|
|
2716
|
+
table_class=tables.DeviceInventoryItemTable,
|
|
2717
|
+
select_related_fields=["manufacturer"],
|
|
2718
|
+
table_filter="device",
|
|
2719
|
+
tab_id="inventory",
|
|
2720
|
+
enable_bulk_actions=True,
|
|
2721
|
+
form_id="inventory-form",
|
|
2722
|
+
footer_buttons=bulk_footer_buttons(form_id="inventory-form", model=InventoryItem),
|
|
2723
|
+
include_paginator=True,
|
|
2724
|
+
),
|
|
2725
|
+
),
|
|
2726
|
+
),
|
|
2727
|
+
DeviceWirelessTab(
|
|
2728
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 1100,
|
|
2729
|
+
tab_id="wireless",
|
|
2730
|
+
label="Wireless",
|
|
2731
|
+
url_name="dcim:device_wireless",
|
|
2732
|
+
related_object_attribute="controller_managed_device_group",
|
|
2733
|
+
panels=(
|
|
2734
|
+
object_detail.ObjectFieldsPanel(
|
|
2735
|
+
weight=100,
|
|
2736
|
+
section=SectionChoices.LEFT_HALF,
|
|
2737
|
+
label="Controller Managed Device Group",
|
|
2738
|
+
fields=["controller_managed_device_group", "controller_managed_device_group__controller"],
|
|
2739
|
+
key_transforms={
|
|
2740
|
+
"controller_managed_device_group": "Name",
|
|
2741
|
+
"controller_managed_device_group__controller": "Controller",
|
|
2742
|
+
},
|
|
2743
|
+
),
|
|
2744
|
+
object_detail.ObjectsTablePanel(
|
|
2745
|
+
weight=200,
|
|
2746
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2747
|
+
table_title="Wireless Networks",
|
|
2748
|
+
table_class=BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable,
|
|
2749
|
+
table_attribute="wireless_network_assignments",
|
|
2750
|
+
related_field_name="controller_device_redundancy_group",
|
|
2751
|
+
tab_id="wireless",
|
|
2752
|
+
include_paginator=True,
|
|
2753
|
+
exclude_columns=["controller_managed_device_group", "controller"],
|
|
2754
|
+
),
|
|
2755
|
+
object_detail.ObjectsTablePanel(
|
|
2756
|
+
weight=300,
|
|
2757
|
+
section=SectionChoices.FULL_WIDTH,
|
|
2758
|
+
table_title="Radio Profiles",
|
|
2759
|
+
table_class=ControllerManagedDeviceGroupRadioProfileAssignmentTable,
|
|
2760
|
+
table_attribute="radio_profile_assignments",
|
|
2761
|
+
related_field_name="controller_device_redundancy_group",
|
|
2762
|
+
tab_id="wireless",
|
|
2763
|
+
include_paginator=True,
|
|
2764
|
+
exclude_columns=["controller_managed_device_group"],
|
|
2765
|
+
),
|
|
2766
|
+
),
|
|
2767
|
+
),
|
|
2768
|
+
DeviceNAPALMTab(
|
|
2769
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 1200,
|
|
2770
|
+
tab_id="status",
|
|
2771
|
+
label="Status",
|
|
2772
|
+
url_name="dcim:device_status",
|
|
2773
|
+
required_permissions=["dcim.napalm_read_device"],
|
|
2774
|
+
),
|
|
2775
|
+
DeviceNAPALMTab(
|
|
2776
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 1300,
|
|
2777
|
+
tab_id="lldp_neighbors",
|
|
2778
|
+
label="LLDP Neighbors",
|
|
2779
|
+
url_name="dcim:device_lldp_neighbors",
|
|
2780
|
+
required_permissions=["dcim.napalm_read_device"],
|
|
2781
|
+
),
|
|
2782
|
+
DeviceNAPALMTab(
|
|
2783
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 1400,
|
|
2784
|
+
tab_id="config",
|
|
2785
|
+
label="Configuration",
|
|
2786
|
+
url_name="dcim:device_config",
|
|
2787
|
+
required_permissions=["dcim.napalm_read_device"],
|
|
2788
|
+
),
|
|
2789
|
+
object_detail.DistinctViewTab(
|
|
2790
|
+
weight=object_detail.Tab.WEIGHT_CHANGELOG_TAB + 1500,
|
|
2791
|
+
tab_id="config_context",
|
|
2792
|
+
label="Config Context",
|
|
2793
|
+
url_name="dcim:device_configcontext",
|
|
2794
|
+
required_permissions=["extras.view_configcontext"],
|
|
2795
|
+
),
|
|
2796
|
+
),
|
|
2797
|
+
)
|
|
2347
2798
|
|
|
2348
2799
|
def get_extra_context(self, request, instance):
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2800
|
+
extra_context = super().get_extra_context(request, instance)
|
|
2801
|
+
|
|
2802
|
+
if self.detail: # TODO: change to `if self.action == "retrieve"` as a part of addressing NAUTOBOT-1051
|
|
2803
|
+
# VirtualChassis members
|
|
2804
|
+
if instance.virtual_chassis is not None:
|
|
2805
|
+
vc_members = (
|
|
2806
|
+
Device.objects.restrict(request.user, "view")
|
|
2807
|
+
.filter(virtual_chassis=instance.virtual_chassis)
|
|
2808
|
+
.order_by("vc_position")
|
|
2809
|
+
)
|
|
2810
|
+
vc_members_table = tables.VirtualChassisMembersTable(vc_members)
|
|
2811
|
+
else:
|
|
2812
|
+
vc_members_table = None
|
|
2813
|
+
extra_context["vc_members_table"] = vc_members_table
|
|
2359
2814
|
|
|
2360
|
-
return
|
|
2361
|
-
**super().get_extra_context(request, instance),
|
|
2362
|
-
"devicebay_table": devicebay_table,
|
|
2363
|
-
"active_tab": "device-bays",
|
|
2364
|
-
}
|
|
2815
|
+
return extra_context
|
|
2365
2816
|
|
|
2817
|
+
@action(
|
|
2818
|
+
detail=True,
|
|
2819
|
+
url_path="dynamic-groups",
|
|
2820
|
+
url_name="dynamicgroups",
|
|
2821
|
+
custom_view_base_action="view",
|
|
2822
|
+
custom_view_additional_permissions=["extras.view_dynamicgroup"],
|
|
2823
|
+
)
|
|
2824
|
+
def dynamic_groups(self, request, *args, **kwargs):
|
|
2825
|
+
return Response({})
|
|
2366
2826
|
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2827
|
+
@action(
|
|
2828
|
+
detail=True,
|
|
2829
|
+
url_path="console-ports",
|
|
2830
|
+
url_name="consoleports",
|
|
2831
|
+
custom_view_base_action="view",
|
|
2832
|
+
custom_view_additional_permissions=["dcim.view_consoleport"],
|
|
2833
|
+
)
|
|
2834
|
+
def console_ports(self, request, *args, **kwargs):
|
|
2835
|
+
return Response({})
|
|
2370
2836
|
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
modulebay_table.columns.show("pk")
|
|
2837
|
+
@action(
|
|
2838
|
+
detail=True,
|
|
2839
|
+
url_path="console-server-ports",
|
|
2840
|
+
url_name="consoleserverports",
|
|
2841
|
+
custom_view_base_action="view",
|
|
2842
|
+
custom_view_additional_permissions=["dcim.view_consoleserverport"],
|
|
2843
|
+
)
|
|
2844
|
+
def console_server_ports(self, request, *args, **kwargs):
|
|
2845
|
+
return Response({})
|
|
2381
2846
|
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2847
|
+
@action(
|
|
2848
|
+
detail=True,
|
|
2849
|
+
url_path="device-bays",
|
|
2850
|
+
url_name="devicebays",
|
|
2851
|
+
custom_view_base_action="view",
|
|
2852
|
+
custom_view_additional_permissions=["dcim.view_devicebay"],
|
|
2853
|
+
)
|
|
2854
|
+
def device_bays(self, request, *args, **kwargs):
|
|
2855
|
+
return Response({})
|
|
2387
2856
|
|
|
2857
|
+
@action(
|
|
2858
|
+
detail=True,
|
|
2859
|
+
url_path="front-ports",
|
|
2860
|
+
url_name="frontports",
|
|
2861
|
+
custom_view_base_action="view",
|
|
2862
|
+
custom_view_additional_permissions=["dcim.view_frontport"],
|
|
2863
|
+
)
|
|
2864
|
+
def front_ports(self, request, *args, **kwargs):
|
|
2865
|
+
return Response({})
|
|
2388
2866
|
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2867
|
+
@action(
|
|
2868
|
+
detail=True,
|
|
2869
|
+
url_path="interfaces",
|
|
2870
|
+
url_name="interfaces",
|
|
2871
|
+
custom_view_base_action="view",
|
|
2872
|
+
custom_view_additional_permissions=["dcim.view_interface"],
|
|
2873
|
+
)
|
|
2874
|
+
def interfaces(self, request, *args, **kwargs):
|
|
2875
|
+
return Response({})
|
|
2392
2876
|
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2877
|
+
@action(
|
|
2878
|
+
detail=True,
|
|
2879
|
+
url_path="inventory",
|
|
2880
|
+
url_name="inventory",
|
|
2881
|
+
custom_view_base_action="view",
|
|
2882
|
+
custom_view_additional_permissions=["dcim.view_inventoryitem"],
|
|
2883
|
+
)
|
|
2884
|
+
def inventory(self, request, *args, **kwargs):
|
|
2885
|
+
return Response({})
|
|
2400
2886
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2887
|
+
@action(
|
|
2888
|
+
detail=True,
|
|
2889
|
+
url_path="module-bays",
|
|
2890
|
+
url_name="modulebays",
|
|
2891
|
+
custom_view_base_action="view",
|
|
2892
|
+
custom_view_additional_permissions=["dcim.view_modulebay"],
|
|
2893
|
+
)
|
|
2894
|
+
def module_bays(self, request, *args, **kwargs):
|
|
2895
|
+
return Response({})
|
|
2405
2896
|
|
|
2897
|
+
@action(
|
|
2898
|
+
detail=True,
|
|
2899
|
+
url_path="power-outlets",
|
|
2900
|
+
url_name="poweroutlets",
|
|
2901
|
+
custom_view_base_action="view",
|
|
2902
|
+
custom_view_additional_permissions=["dcim.view_poweroutlet"],
|
|
2903
|
+
)
|
|
2904
|
+
def power_outlets(self, request, *args, **kwargs):
|
|
2905
|
+
return Response({})
|
|
2406
2906
|
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2907
|
+
@action(
|
|
2908
|
+
detail=True,
|
|
2909
|
+
url_path="power-ports",
|
|
2910
|
+
url_name="powerports",
|
|
2911
|
+
custom_view_base_action="view",
|
|
2912
|
+
custom_view_additional_permissions=["dcim.view_powerport"],
|
|
2913
|
+
)
|
|
2914
|
+
def power_ports(self, request, *args, **kwargs):
|
|
2915
|
+
return Response({})
|
|
2411
2916
|
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2917
|
+
@action(
|
|
2918
|
+
detail=True,
|
|
2919
|
+
url_path="rear-ports",
|
|
2920
|
+
url_name="rearports",
|
|
2921
|
+
custom_view_base_action="view",
|
|
2922
|
+
custom_view_additional_permissions=["dcim.view_rearport"],
|
|
2923
|
+
)
|
|
2924
|
+
def rear_ports(self, request, *args, **kwargs):
|
|
2925
|
+
return Response({})
|
|
2416
2926
|
|
|
2927
|
+
@action(
|
|
2928
|
+
detail=True,
|
|
2929
|
+
url_path="wireless",
|
|
2930
|
+
url_name="wireless",
|
|
2931
|
+
custom_view_base_action="view",
|
|
2932
|
+
custom_view_additional_permissions=["dcim.view_controllermanageddevicegroup"],
|
|
2933
|
+
)
|
|
2934
|
+
def wireless(self, request, *args, **kwargs):
|
|
2935
|
+
return Response({})
|
|
2417
2936
|
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2937
|
+
@action(
|
|
2938
|
+
detail=True,
|
|
2939
|
+
url_path="status",
|
|
2940
|
+
url_name="status",
|
|
2941
|
+
custom_view_base_action="view",
|
|
2942
|
+
custom_view_additional_permissions=["dcim.napalm_read_device"],
|
|
2943
|
+
)
|
|
2944
|
+
def status(self, request, *args, **kwargs):
|
|
2945
|
+
return Response(
|
|
2946
|
+
{
|
|
2947
|
+
"template": "dcim/device/status.html",
|
|
2948
|
+
},
|
|
2949
|
+
)
|
|
2422
2950
|
|
|
2423
|
-
|
|
2951
|
+
@action(
|
|
2952
|
+
detail=True,
|
|
2953
|
+
url_path="lldp-neighbors",
|
|
2954
|
+
url_name="lldp_neighbors",
|
|
2955
|
+
custom_view_base_action="view",
|
|
2956
|
+
custom_view_additional_permissions=["dcim.napalm_read_device"],
|
|
2957
|
+
)
|
|
2958
|
+
def lldp_neighbors(self, request, *args, **kwargs):
|
|
2959
|
+
instance = self.get_object()
|
|
2424
2960
|
interfaces = (
|
|
2425
2961
|
instance.all_interfaces.restrict(request.user, "view")
|
|
2426
2962
|
.prefetch_related("_path__destination")
|
|
2427
2963
|
.exclude(type__in=NONCONNECTABLE_IFACE_TYPES)
|
|
2428
2964
|
)
|
|
2965
|
+
return Response(
|
|
2966
|
+
{
|
|
2967
|
+
"template": "dcim/device/lldp_neighbors.html",
|
|
2968
|
+
"interfaces": interfaces,
|
|
2969
|
+
},
|
|
2970
|
+
)
|
|
2429
2971
|
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
class DeviceConfigView(generic.ObjectView):
|
|
2437
|
-
additional_permissions = ["dcim.napalm_read_device"]
|
|
2438
|
-
queryset = Device.objects.all()
|
|
2439
|
-
template_name = "dcim/device/config.html"
|
|
2440
|
-
|
|
2441
|
-
def get_extra_context(self, request, instance):
|
|
2442
|
-
return {
|
|
2443
|
-
"active_tab": "config",
|
|
2444
|
-
}
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
class DeviceConfigContextView(ObjectConfigContextView):
|
|
2448
|
-
base_template = "dcim/device/base.html"
|
|
2449
|
-
|
|
2450
|
-
@cached_property
|
|
2451
|
-
def queryset(self): # pylint: disable=method-hidden
|
|
2452
|
-
"""
|
|
2453
|
-
A cached_property rather than a class attribute because annotate_config_context_data() is unsafe at import time.
|
|
2454
|
-
"""
|
|
2455
|
-
return Device.objects.annotate_config_context_data()
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
class DeviceChangeLogView(ObjectChangeLogView):
|
|
2459
|
-
base_template = "dcim/device/base.html"
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
class DeviceDynamicGroupsView(ObjectDynamicGroupsView):
|
|
2463
|
-
base_template = "dcim/device/base.html"
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
class DeviceEditView(generic.ObjectEditView):
|
|
2467
|
-
queryset = Device.objects.all()
|
|
2468
|
-
model_form = forms.DeviceForm
|
|
2469
|
-
template_name = "dcim/device_edit.html"
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
class DeviceDeleteView(generic.ObjectDeleteView):
|
|
2473
|
-
queryset = Device.objects.all()
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
class DeviceBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
|
|
2477
|
-
queryset = Device.objects.all()
|
|
2478
|
-
table = tables.DeviceImportTable
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
class DeviceBulkEditView(generic.BulkEditView):
|
|
2482
|
-
queryset = Device.objects.select_related(
|
|
2483
|
-
"tenant",
|
|
2484
|
-
"location",
|
|
2485
|
-
"rack",
|
|
2486
|
-
"role",
|
|
2487
|
-
"device_type__manufacturer",
|
|
2488
|
-
"secrets_group",
|
|
2489
|
-
"device_redundancy_group",
|
|
2490
|
-
"controller_managed_device_group",
|
|
2972
|
+
@action(
|
|
2973
|
+
detail=True,
|
|
2974
|
+
url_path="config",
|
|
2975
|
+
url_name="config",
|
|
2976
|
+
custom_view_base_action="view",
|
|
2977
|
+
custom_view_additional_permissions=["dcim.napalm_read_device"],
|
|
2491
2978
|
)
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
class DeviceBulkDeleteView(generic.BulkDeleteView):
|
|
2498
|
-
queryset = Device.objects.select_related("tenant", "location", "rack", "role", "device_type__manufacturer")
|
|
2499
|
-
filterset = filters.DeviceFilterSet
|
|
2500
|
-
table = tables.DeviceTable
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
class DeviceWirelessView(generic.ObjectView):
|
|
2504
|
-
queryset = Device.objects.all()
|
|
2505
|
-
template_name = "dcim/device/wireless.html"
|
|
2506
|
-
|
|
2507
|
-
def get_extra_context(self, request, instance):
|
|
2508
|
-
controller_managed_device_group = instance.controller_managed_device_group
|
|
2509
|
-
wireless_networks = ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.filter(
|
|
2510
|
-
controller_managed_device_group=controller_managed_device_group
|
|
2511
|
-
).select_related("wireless_network", "controller_managed_device_group", "vlan")
|
|
2512
|
-
wireless_networks_table = BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable(
|
|
2513
|
-
data=wireless_networks, user=request.user, orderable=False
|
|
2514
|
-
)
|
|
2515
|
-
wireless_networks_table.columns.hide("controller_managed_device_group")
|
|
2516
|
-
wireless_networks_table.columns.hide("controller")
|
|
2517
|
-
RequestConfig(
|
|
2518
|
-
request, paginate={"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
2519
|
-
).configure(wireless_networks_table)
|
|
2520
|
-
|
|
2521
|
-
radio_profiles = ControllerManagedDeviceGroupRadioProfileAssignment.objects.filter(
|
|
2522
|
-
controller_managed_device_group=controller_managed_device_group
|
|
2523
|
-
).select_related("radio_profile", "controller_managed_device_group")
|
|
2524
|
-
radio_profiles_table = ControllerManagedDeviceGroupRadioProfileAssignmentTable(
|
|
2525
|
-
data=radio_profiles, user=request.user, orderable=False
|
|
2979
|
+
def config(self, request, *args, **kwargs):
|
|
2980
|
+
return Response(
|
|
2981
|
+
{
|
|
2982
|
+
"template": "dcim/device/config.html",
|
|
2983
|
+
},
|
|
2526
2984
|
)
|
|
2527
|
-
radio_profiles_table.columns.hide("controller_managed_device_group")
|
|
2528
|
-
RequestConfig(
|
|
2529
|
-
request, paginate={"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
2530
|
-
).configure(radio_profiles_table)
|
|
2531
2985
|
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2986
|
+
@action(
|
|
2987
|
+
detail=True,
|
|
2988
|
+
url_path="config-context",
|
|
2989
|
+
url_name="configcontext",
|
|
2990
|
+
custom_view_base_action="view",
|
|
2991
|
+
custom_view_additional_permissions=["extras.view_configcontext"],
|
|
2992
|
+
)
|
|
2993
|
+
def config_context(self, request, *args, **kwargs):
|
|
2994
|
+
instance = self.get_object()
|
|
2995
|
+
|
|
2996
|
+
# Determine user's preferred output format
|
|
2997
|
+
if request.GET.get("data_format") in ["json", "yaml"]:
|
|
2998
|
+
data_format = request.GET.get("data_format")
|
|
2999
|
+
if request.user.is_authenticated:
|
|
3000
|
+
request.user.set_config("extras.configcontext.format", data_format, commit=True)
|
|
3001
|
+
elif request.user.is_authenticated:
|
|
3002
|
+
data_format = request.user.get_config("extras.configcontext.format", "json")
|
|
3003
|
+
else:
|
|
3004
|
+
data_format = "json"
|
|
3005
|
+
|
|
3006
|
+
context = {
|
|
3007
|
+
"object": instance,
|
|
3008
|
+
"content_type": ContentType.objects.get_for_model(self.queryset.model),
|
|
3009
|
+
"verbose_name": self.queryset.model._meta.verbose_name,
|
|
3010
|
+
"verbose_name_plural": self.queryset.model._meta.verbose_name_plural,
|
|
3011
|
+
"object_detail_content": self.object_detail_content,
|
|
3012
|
+
**common_detail_view_context(request, instance),
|
|
3013
|
+
"rendered_context": instance.get_config_context(),
|
|
3014
|
+
"source_contexts": ConfigContext.objects.restrict(request.user, "view").get_for_object(instance),
|
|
3015
|
+
"format": data_format,
|
|
3016
|
+
"template": "extras/object_configcontext.html",
|
|
3017
|
+
"base_template": "dcim/device.html",
|
|
2536
3018
|
}
|
|
2537
3019
|
|
|
3020
|
+
return Response(context)
|
|
3021
|
+
|
|
2538
3022
|
|
|
2539
3023
|
#
|
|
2540
3024
|
# Modules
|
|
@@ -3007,7 +3491,7 @@ class ConsolePortListView(generic.ObjectListView):
|
|
|
3007
3491
|
action_buttons = ("import", "export")
|
|
3008
3492
|
|
|
3009
3493
|
|
|
3010
|
-
class ConsolePortView(generic.ObjectView):
|
|
3494
|
+
class ConsolePortView(DeviceComponentPageMixin, generic.ObjectView):
|
|
3011
3495
|
queryset = ConsolePort.objects.all()
|
|
3012
3496
|
|
|
3013
3497
|
def get_extra_context(self, request, instance):
|
|
@@ -3073,7 +3557,7 @@ class ConsoleServerPortListView(generic.ObjectListView):
|
|
|
3073
3557
|
action_buttons = ("import", "export")
|
|
3074
3558
|
|
|
3075
3559
|
|
|
3076
|
-
class ConsoleServerPortView(generic.ObjectView):
|
|
3560
|
+
class ConsoleServerPortView(DeviceComponentPageMixin, generic.ObjectView):
|
|
3077
3561
|
queryset = ConsoleServerPort.objects.all()
|
|
3078
3562
|
|
|
3079
3563
|
def get_extra_context(self, request, instance):
|
|
@@ -3139,7 +3623,7 @@ class PowerPortListView(generic.ObjectListView):
|
|
|
3139
3623
|
action_buttons = ("import", "export")
|
|
3140
3624
|
|
|
3141
3625
|
|
|
3142
|
-
class PowerPortView(generic.ObjectView):
|
|
3626
|
+
class PowerPortView(DeviceComponentPageMixin, generic.ObjectView):
|
|
3143
3627
|
queryset = PowerPort.objects.all()
|
|
3144
3628
|
|
|
3145
3629
|
def get_extra_context(self, request, instance):
|
|
@@ -3205,7 +3689,7 @@ class PowerOutletListView(generic.ObjectListView):
|
|
|
3205
3689
|
action_buttons = ("import", "export")
|
|
3206
3690
|
|
|
3207
3691
|
|
|
3208
|
-
class PowerOutletView(generic.ObjectView):
|
|
3692
|
+
class PowerOutletView(DeviceComponentPageMixin, generic.ObjectView):
|
|
3209
3693
|
queryset = PowerOutlet.objects.all()
|
|
3210
3694
|
|
|
3211
3695
|
def get_extra_context(self, request, instance):
|
|
@@ -3271,7 +3755,10 @@ class InterfaceListView(generic.ObjectListView):
|
|
|
3271
3755
|
action_buttons = ("import", "export")
|
|
3272
3756
|
|
|
3273
3757
|
|
|
3274
|
-
class InterfaceView(
|
|
3758
|
+
class InterfaceView(
|
|
3759
|
+
DeviceComponentPageMixin,
|
|
3760
|
+
generic.ObjectView,
|
|
3761
|
+
):
|
|
3275
3762
|
queryset = Interface.objects.all()
|
|
3276
3763
|
|
|
3277
3764
|
def get_extra_context(self, request, instance):
|
|
@@ -3401,7 +3888,7 @@ class FrontPortListView(generic.ObjectListView):
|
|
|
3401
3888
|
action_buttons = ("import", "export")
|
|
3402
3889
|
|
|
3403
3890
|
|
|
3404
|
-
class FrontPortView(generic.ObjectView):
|
|
3891
|
+
class FrontPortView(DeviceComponentPageMixin, generic.ObjectView):
|
|
3405
3892
|
queryset = FrontPort.objects.all()
|
|
3406
3893
|
|
|
3407
3894
|
def get_extra_context(self, request, instance):
|
|
@@ -3467,7 +3954,7 @@ class RearPortListView(generic.ObjectListView):
|
|
|
3467
3954
|
action_buttons = ("import", "export")
|
|
3468
3955
|
|
|
3469
3956
|
|
|
3470
|
-
class RearPortView(generic.ObjectView):
|
|
3957
|
+
class RearPortView(DeviceComponentPageMixin, generic.ObjectView):
|
|
3471
3958
|
queryset = RearPort.objects.all()
|
|
3472
3959
|
|
|
3473
3960
|
def get_extra_context(self, request, instance):
|
|
@@ -3533,7 +4020,7 @@ class DeviceBayListView(generic.ObjectListView):
|
|
|
3533
4020
|
action_buttons = ("import", "export")
|
|
3534
4021
|
|
|
3535
4022
|
|
|
3536
|
-
class DeviceBayView(generic.ObjectView):
|
|
4023
|
+
class DeviceBayView(DeviceComponentPageMixin, generic.ObjectView):
|
|
3537
4024
|
queryset = DeviceBay.objects.all()
|
|
3538
4025
|
|
|
3539
4026
|
def get_extra_context(self, request, instance):
|
|
@@ -3695,6 +4182,36 @@ class ModuleBayUIViewSet(ModuleBayCommonViewSetMixin, NautobotUIViewSet):
|
|
|
3695
4182
|
),
|
|
3696
4183
|
)
|
|
3697
4184
|
)
|
|
4185
|
+
breadcrumbs = Breadcrumbs(
|
|
4186
|
+
items={
|
|
4187
|
+
"detail": [
|
|
4188
|
+
# Breadcrumb path if ModuleBay is linked with device
|
|
4189
|
+
ModelBreadcrumbItem(model=Device, should_render=lambda c: c["object"].parent_device),
|
|
4190
|
+
InstanceBreadcrumbItem(
|
|
4191
|
+
instance=context_object_attr("parent_device"),
|
|
4192
|
+
should_render=context_object_attr("parent_device"),
|
|
4193
|
+
),
|
|
4194
|
+
ViewNameBreadcrumbItem(
|
|
4195
|
+
view_name_key="device_breadcrumb_url",
|
|
4196
|
+
should_render=lambda c: c["object"].parent_device and c.get("device_breadcrumb_url"),
|
|
4197
|
+
reverse_kwargs=lambda c: {"pk": c["object"].parent_device.pk},
|
|
4198
|
+
label=lambda c: c["object"]._meta.verbose_name_plural,
|
|
4199
|
+
),
|
|
4200
|
+
# Breadcrumb path if ModuleBay is linked with module
|
|
4201
|
+
ModelBreadcrumbItem(model=Module, should_render=lambda c: c["object"].parent_device is None),
|
|
4202
|
+
InstanceBreadcrumbItem(
|
|
4203
|
+
instance=context_object_attr("parent_module"),
|
|
4204
|
+
should_render=lambda c: c["object"].parent_device is None,
|
|
4205
|
+
),
|
|
4206
|
+
ViewNameBreadcrumbItem(
|
|
4207
|
+
view_name_key="module_breadcrumb_url",
|
|
4208
|
+
should_render=lambda c: c["object"].parent_device is None and c.get("module_breadcrumb_url"),
|
|
4209
|
+
reverse_kwargs=lambda c: {"pk": c["object"].parent_module.pk},
|
|
4210
|
+
label=lambda c: c["object"]._meta.verbose_name_plural,
|
|
4211
|
+
),
|
|
4212
|
+
]
|
|
4213
|
+
}
|
|
4214
|
+
)
|
|
3698
4215
|
|
|
3699
4216
|
def get_extra_context(self, request, instance):
|
|
3700
4217
|
context = super().get_extra_context(request, instance)
|
|
@@ -3748,7 +4265,7 @@ class InventoryItemListView(generic.ObjectListView):
|
|
|
3748
4265
|
action_buttons = ("import", "export")
|
|
3749
4266
|
|
|
3750
4267
|
|
|
3751
|
-
class InventoryItemView(generic.ObjectView):
|
|
4268
|
+
class InventoryItemView(DeviceComponentPageMixin, generic.ObjectView):
|
|
3752
4269
|
queryset = InventoryItem.objects.all().select_related("device", "manufacturer", "software_version")
|
|
3753
4270
|
|
|
3754
4271
|
def get_extra_context(self, request, instance):
|
|
@@ -4108,6 +4625,10 @@ class ConsoleConnectionsListView(ConnectionsListView):
|
|
|
4108
4625
|
table = tables.ConsoleConnectionTable
|
|
4109
4626
|
template_name = "dcim/console_port_connection_list.html"
|
|
4110
4627
|
action_buttons = ("export",)
|
|
4628
|
+
view_titles = Titles(titles={"list": "Console Connections"})
|
|
4629
|
+
breadcrumbs = Breadcrumbs(
|
|
4630
|
+
items={"list": [ViewNameBreadcrumbItem(view_name="dcim:console_connections_list", label="Console Connections")]}
|
|
4631
|
+
)
|
|
4111
4632
|
|
|
4112
4633
|
def extra_context(self):
|
|
4113
4634
|
return {
|
|
@@ -4124,6 +4645,10 @@ class PowerConnectionsListView(ConnectionsListView):
|
|
|
4124
4645
|
table = tables.PowerConnectionTable
|
|
4125
4646
|
template_name = "dcim/power_port_connection_list.html"
|
|
4126
4647
|
action_buttons = ("export",)
|
|
4648
|
+
view_titles = Titles(titles={"list": "Power Connections"})
|
|
4649
|
+
breadcrumbs = Breadcrumbs(
|
|
4650
|
+
items={"list": [ViewNameBreadcrumbItem(view_name="dcim:power_connections_list", label="Power Connections")]}
|
|
4651
|
+
)
|
|
4127
4652
|
|
|
4128
4653
|
def extra_context(self):
|
|
4129
4654
|
return {
|
|
@@ -4140,6 +4665,12 @@ class InterfaceConnectionsListView(ConnectionsListView):
|
|
|
4140
4665
|
table = tables.InterfaceConnectionTable
|
|
4141
4666
|
template_name = "dcim/interface_connection_list.html"
|
|
4142
4667
|
action_buttons = ("export",)
|
|
4668
|
+
view_titles = Titles(titles={"list": "Interface Connections"})
|
|
4669
|
+
breadcrumbs = Breadcrumbs(
|
|
4670
|
+
items={
|
|
4671
|
+
"list": [ViewNameBreadcrumbItem(view_name="dcim:interface_connections_list", label="Interface Connections")]
|
|
4672
|
+
}
|
|
4673
|
+
)
|
|
4143
4674
|
|
|
4144
4675
|
def __init__(self, *args, **kwargs):
|
|
4145
4676
|
super().__init__(*args, **kwargs)
|
|
@@ -4182,10 +4713,50 @@ class VirtualChassisUIViewSet(NautobotUIViewSet):
|
|
|
4182
4713
|
bulk_update_form_class = forms.VirtualChassisBulkEditForm
|
|
4183
4714
|
filterset_class = filters.VirtualChassisFilterSet
|
|
4184
4715
|
filterset_form_class = forms.VirtualChassisFilterForm
|
|
4185
|
-
form_class = forms.VirtualChassisCreateForm
|
|
4186
4716
|
serializer_class = serializers.VirtualChassisSerializer
|
|
4187
4717
|
table_class = tables.VirtualChassisTable
|
|
4188
4718
|
queryset = VirtualChassis.objects.all()
|
|
4719
|
+
create_form_class = forms.VirtualChassisCreateForm
|
|
4720
|
+
update_form_class = forms.VirtualChassisForm
|
|
4721
|
+
|
|
4722
|
+
class MembersObjectsTablePanel(object_detail.ObjectsTablePanel):
|
|
4723
|
+
def _get_table_add_url(self, context):
|
|
4724
|
+
obj = get_obj_from_context(context)
|
|
4725
|
+
request = context["request"]
|
|
4726
|
+
return_url = context.get("return_url", obj.get_absolute_url())
|
|
4727
|
+
|
|
4728
|
+
if not request.user.has_perm("dcim.change_virtualchassis"):
|
|
4729
|
+
return None
|
|
4730
|
+
|
|
4731
|
+
params = []
|
|
4732
|
+
master = obj.master
|
|
4733
|
+
|
|
4734
|
+
if master is not None:
|
|
4735
|
+
if master.location is not None:
|
|
4736
|
+
params.append(("location", master.location.pk))
|
|
4737
|
+
|
|
4738
|
+
if master.rack is not None:
|
|
4739
|
+
params.append(("rack", master.rack.pk))
|
|
4740
|
+
|
|
4741
|
+
params.append(("return_url", return_url))
|
|
4742
|
+
return reverse("dcim:virtualchassis_add_member", kwargs={"pk": obj.pk}) + "?" + urlencode(params)
|
|
4743
|
+
|
|
4744
|
+
object_detail_content = object_detail.ObjectDetailContent(
|
|
4745
|
+
panels=[
|
|
4746
|
+
object_detail.ObjectFieldsPanel(
|
|
4747
|
+
section=SectionChoices.LEFT_HALF,
|
|
4748
|
+
weight=100,
|
|
4749
|
+
fields="__all__",
|
|
4750
|
+
),
|
|
4751
|
+
MembersObjectsTablePanel(
|
|
4752
|
+
section=SectionChoices.RIGHT_HALF,
|
|
4753
|
+
weight=100,
|
|
4754
|
+
table_class=tables.VirtualChassisMembersTable,
|
|
4755
|
+
table_filter="virtual_chassis",
|
|
4756
|
+
table_title="Members",
|
|
4757
|
+
),
|
|
4758
|
+
]
|
|
4759
|
+
)
|
|
4189
4760
|
|
|
4190
4761
|
def get_extra_context(self, request, instance):
|
|
4191
4762
|
context = super().get_extra_context(request, instance)
|
|
@@ -4215,10 +4786,6 @@ class VirtualChassisUIViewSet(NautobotUIViewSet):
|
|
|
4215
4786
|
}
|
|
4216
4787
|
)
|
|
4217
4788
|
|
|
4218
|
-
elif self.action == "retrieve":
|
|
4219
|
-
members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
|
|
4220
|
-
context.update({"members": members})
|
|
4221
|
-
|
|
4222
4789
|
return context
|
|
4223
4790
|
|
|
4224
4791
|
def form_save(self, form, **kwargs):
|
|
@@ -4355,6 +4922,17 @@ class PowerPanelUIViewSet(NautobotUIViewSet):
|
|
|
4355
4922
|
serializer_class = serializers.PowerPanelSerializer
|
|
4356
4923
|
table_class = tables.PowerPanelTable
|
|
4357
4924
|
queryset = PowerPanel.objects.all()
|
|
4925
|
+
breadcrumbs = Breadcrumbs(
|
|
4926
|
+
items={
|
|
4927
|
+
"detail": [
|
|
4928
|
+
ModelBreadcrumbItem(),
|
|
4929
|
+
InstanceBreadcrumbItem(instance=context_object_attr("location")),
|
|
4930
|
+
InstanceBreadcrumbItem(
|
|
4931
|
+
instance=context_object_attr("rack_group"), should_render=context_object_attr("rack_group")
|
|
4932
|
+
),
|
|
4933
|
+
]
|
|
4934
|
+
}
|
|
4935
|
+
)
|
|
4358
4936
|
|
|
4359
4937
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
4360
4938
|
panels=(
|
|
@@ -4429,6 +5007,19 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
|
|
|
4429
5007
|
queryset = PowerFeed.objects.all()
|
|
4430
5008
|
serializer_class = serializers.PowerFeedSerializer
|
|
4431
5009
|
table_class = tables.PowerFeedTable
|
|
5010
|
+
breadcrumbs = Breadcrumbs(
|
|
5011
|
+
items={
|
|
5012
|
+
"detail": [
|
|
5013
|
+
ModelBreadcrumbItem(),
|
|
5014
|
+
InstanceBreadcrumbItem(instance=context_object_attr("power_panel.location")),
|
|
5015
|
+
InstanceBreadcrumbItem(instance=context_object_attr("power_panel")),
|
|
5016
|
+
InstanceBreadcrumbItem(
|
|
5017
|
+
instance=context_object_attr("rack"),
|
|
5018
|
+
should_render=context_object_attr("rack"),
|
|
5019
|
+
),
|
|
5020
|
+
]
|
|
5021
|
+
}
|
|
5022
|
+
)
|
|
4432
5023
|
|
|
4433
5024
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
4434
5025
|
panels=(
|
|
@@ -5145,6 +5736,14 @@ class VirtualDeviceContextUIViewSet(NautobotUIViewSet):
|
|
|
5145
5736
|
queryset = VirtualDeviceContext.objects.all()
|
|
5146
5737
|
serializer_class = serializers.VirtualDeviceContextSerializer
|
|
5147
5738
|
table_class = tables.VirtualDeviceContextTable
|
|
5739
|
+
breadcrumbs = Breadcrumbs(
|
|
5740
|
+
items={
|
|
5741
|
+
"detail": [
|
|
5742
|
+
ModelBreadcrumbItem(),
|
|
5743
|
+
InstanceBreadcrumbItem(instance=context_object_attr("device")),
|
|
5744
|
+
]
|
|
5745
|
+
}
|
|
5746
|
+
)
|
|
5148
5747
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
5149
5748
|
panels=(
|
|
5150
5749
|
object_detail.ObjectFieldsPanel(
|