nautobot 2.1.7__py3-none-any.whl → 2.1.9__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/api.py +1 -2
- nautobot/apps/utils.py +4 -0
- nautobot/apps/views.py +2 -0
- nautobot/circuits/api/urls.py +1 -2
- nautobot/circuits/api/views.py +0 -12
- nautobot/circuits/tests/integration/test_relationships.py +0 -4
- nautobot/core/api/routers.py +25 -3
- nautobot/core/api/utils.py +4 -0
- nautobot/core/api/views.py +21 -15
- nautobot/core/celery/schedulers.py +13 -0
- nautobot/core/choices.py +0 -21
- nautobot/core/models/__init__.py +1 -1
- nautobot/core/models/tree_queries.py +29 -7
- nautobot/core/releases.py +1 -1
- nautobot/core/settings.py +9 -0
- nautobot/core/settings_funcs.py +0 -18
- nautobot/core/signals.py +5 -5
- nautobot/core/tasks.py +7 -3
- nautobot/core/templates/admin/base.html +23 -94
- nautobot/core/templates/generic/object_list.html +2 -0
- nautobot/core/templates/graphene/graphiql.html +18 -47
- nautobot/core/templates/inc/footer.html +5 -5
- nautobot/core/templates/inc/nav_menu.html +0 -7
- nautobot/core/templates/nautobot_config.py.j2 +6 -0
- nautobot/core/templates/rest_framework/api.html +12 -5
- nautobot/core/testing/mixins.py +13 -5
- nautobot/core/tests/integration/test_plugin_navbar.py +7 -21
- nautobot/core/tests/integration/test_view_authentication.py +67 -0
- nautobot/core/tests/runner.py +25 -2
- nautobot/core/tests/test_graphql.py +2 -14
- nautobot/core/tests/test_models.py +3 -3
- nautobot/core/tests/test_navigations.py +67 -10
- nautobot/core/tests/test_releases.py +9 -3
- nautobot/core/tests/test_views.py +23 -16
- nautobot/core/utils/lookup.py +124 -0
- nautobot/core/views/__init__.py +3 -7
- nautobot/core/views/generic.py +9 -0
- nautobot/dcim/api/urls.py +1 -2
- nautobot/dcim/api/views.py +1 -12
- nautobot/dcim/choices.py +56 -0
- nautobot/dcim/models/racks.py +1 -3
- nautobot/dcim/navigation.py +1 -1
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +67 -43
- nautobot/dcim/tests/test_api.py +3 -0
- nautobot/dcim/tests/test_filters.py +0 -28
- nautobot/dcim/views.py +5 -2
- nautobot/extras/api/urls.py +1 -2
- nautobot/extras/api/views.py +0 -10
- nautobot/extras/choices.py +14 -0
- nautobot/extras/models/customfields.py +93 -34
- nautobot/extras/models/groups.py +1 -1
- nautobot/extras/models/relationships.py +32 -19
- nautobot/extras/navigation.py +3 -2
- nautobot/extras/plugins/__init__.py +8 -0
- nautobot/extras/plugins/views.py +6 -9
- nautobot/extras/querysets.py +1 -1
- nautobot/extras/signals.py +12 -6
- nautobot/extras/templates/extras/customfield.html +22 -14
- nautobot/extras/templatetags/job_buttons.py +7 -0
- nautobot/extras/templatetags/plugins.py +5 -1
- nautobot/extras/tests/test_customfields.py +323 -287
- nautobot/extras/tests/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/test_jobs.py +2 -2
- nautobot/extras/tests/test_plugins.py +41 -0
- nautobot/extras/tests/test_relationships.py +31 -14
- nautobot/extras/tests/test_views.py +124 -1
- nautobot/extras/utils.py +7 -3
- nautobot/extras/views.py +10 -10
- nautobot/ipam/api/urls.py +1 -2
- nautobot/ipam/api/views.py +6 -13
- nautobot/ipam/tables.py +0 -1
- nautobot/ipam/tests/test_graphql.py +2 -3
- nautobot/ipam/views.py +12 -10
- nautobot/project-static/css/base.css +1 -0
- nautobot/project-static/docs/404.html +30 -2
- nautobot/project-static/docs/apps/index.html +30 -2
- nautobot/project-static/docs/apps/nautobot-apps.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +410 -410
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +386 -358
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +45 -17
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +759 -602
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +528 -467
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +205 -109
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +1265 -785
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1827 -1746
- nautobot/project-static/docs/development/apps/api/configuration-view.html +30 -2
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +30 -2
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +30 -2
- nautobot/project-static/docs/development/apps/api/models/global-search.html +30 -2
- nautobot/project-static/docs/development/apps/api/models/graphql.html +30 -2
- nautobot/project-static/docs/development/apps/api/models/index.html +30 -2
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +31 -3
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +30 -2
- nautobot/project-static/docs/development/apps/api/prometheus.html +30 -2
- nautobot/project-static/docs/development/apps/api/setup.html +30 -2
- nautobot/project-static/docs/development/apps/api/testing.html +33 -5
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +30 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +30 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +30 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +33 -5
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-detail-views.html +13 -5559
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +5594 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/tabs.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/base-template.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +44 -11
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +47 -14
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/index.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/notes.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/urls.html +30 -2
- nautobot/project-static/docs/development/apps/index.html +30 -2
- nautobot/project-static/docs/development/apps/migration/code-updates.html +30 -2
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +30 -2
- nautobot/project-static/docs/development/apps/migration/from-v1.html +30 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +30 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +30 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +30 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +30 -2
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +30 -2
- nautobot/project-static/docs/development/core/application-registry.html +30 -2
- nautobot/project-static/docs/development/core/best-practices.html +33 -5
- nautobot/project-static/docs/development/core/bootstrap-ui.html +30 -2
- nautobot/project-static/docs/development/core/caching.html +5481 -0
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +30 -2
- nautobot/project-static/docs/development/core/extending-models.html +33 -5
- nautobot/project-static/docs/development/core/generic-views.html +30 -2
- nautobot/project-static/docs/development/core/getting-started.html +49 -12
- nautobot/project-static/docs/development/core/homepage.html +30 -2
- nautobot/project-static/docs/development/core/index.html +30 -2
- nautobot/project-static/docs/development/core/model-features.html +30 -2
- nautobot/project-static/docs/development/core/natural-keys.html +30 -2
- nautobot/project-static/docs/development/core/navigation-menu.html +30 -2
- nautobot/project-static/docs/development/core/release-checklist.html +30 -2
- nautobot/project-static/docs/development/core/role-internals.html +30 -2
- nautobot/project-static/docs/development/core/style-guide.html +30 -2
- nautobot/project-static/docs/development/core/templates.html +30 -2
- nautobot/project-static/docs/development/core/testing.html +30 -2
- nautobot/project-static/docs/development/core/user-preferences.html +30 -2
- nautobot/project-static/docs/development/index.html +30 -2
- nautobot/project-static/docs/development/jobs/index.html +30 -2
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +30 -2
- nautobot/project-static/docs/index.html +30 -2
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/release-notes/index.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.0.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.1.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.2.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.3.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.4.html +31 -3
- nautobot/project-static/docs/release-notes/version-1.5.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.6.html +573 -134
- nautobot/project-static/docs/release-notes/version-2.0.html +30 -2
- nautobot/project-static/docs/release-notes/version-2.1.html +539 -170
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +250 -240
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +49 -2
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/docker.html +37 -5
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +6019 -0
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/index.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +33 -5
- nautobot/project-static/docs/user-guide/administration/installation/services.html +30 -2
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +30 -2
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +30 -2
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +30 -2
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +33 -5
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +30 -2
- nautobot/project-static/docs/user-guide/index.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +111 -15
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +30 -2
- nautobot/tenancy/api/urls.py +1 -2
- nautobot/tenancy/api/views.py +0 -12
- nautobot/tenancy/navigation.py +1 -1
- nautobot/tenancy/tests/test_filters.py +0 -168
- nautobot/users/api/urls.py +1 -2
- nautobot/users/api/views.py +2 -65
- nautobot/users/views.py +8 -8
- nautobot/virtualization/api/urls.py +1 -2
- nautobot/virtualization/api/views.py +0 -12
- nautobot/virtualization/tests/test_filters.py +0 -28
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/METADATA +2 -2
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/RECORD +338 -334
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/NOTICE +0 -0
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/WHEEL +0 -0
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/entry_points.txt +0 -0
|
@@ -214,6 +214,7 @@ class FilterFormsTestCase(TestCase):
|
|
|
214
214
|
)
|
|
215
215
|
url = reverse("dcim:location_list") + query_param
|
|
216
216
|
response = self.client.get(url)
|
|
217
|
+
self.assertHttpStatus(response, 200)
|
|
217
218
|
response_content = response.content.decode(response.charset).replace("\n", "")
|
|
218
219
|
self.assertInHTML(locations[0].name, response_content)
|
|
219
220
|
self.assertInHTML(locations[1].name, response_content)
|
|
@@ -314,25 +315,31 @@ class LoginUI(TestCase):
|
|
|
314
315
|
sso_login_search_result = self.make_request()
|
|
315
316
|
self.assertIsNotNone(sso_login_search_result)
|
|
316
317
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
"""Assert that api docs and graphql redirects to login page if user is unauthenticated."""
|
|
318
|
+
def test_graphql_redirects_back_to_login_unauthenticated(self):
|
|
319
|
+
"""Assert that graphql redirects to login page if user is unauthenticated."""
|
|
320
320
|
self.client.logout()
|
|
321
321
|
headers = {"HTTP_ACCEPT": "text/html"}
|
|
322
|
-
|
|
322
|
+
url = reverse("graphql")
|
|
323
|
+
response = self.client.get(url, follow=True, **headers)
|
|
324
|
+
self.assertHttpStatus(response, 200)
|
|
325
|
+
self.assertRedirects(response, f"/login/?next={url}")
|
|
326
|
+
response_content = response.content.decode(response.charset).replace("\n", "")
|
|
327
|
+
for footer_text in self.footer_elements:
|
|
328
|
+
self.assertNotIn(footer_text, response_content)
|
|
329
|
+
|
|
330
|
+
def test_api_docs_403_unauthenticated(self):
|
|
331
|
+
"""Assert that api docs return a 403 Forbidden if user is unauthenticated."""
|
|
332
|
+
self.client.logout()
|
|
333
|
+
urls = [
|
|
334
|
+
reverse("api_docs"),
|
|
335
|
+
reverse("api_redocs"),
|
|
336
|
+
reverse("schema"),
|
|
337
|
+
reverse("schema_json"),
|
|
338
|
+
reverse("schema_yaml"),
|
|
339
|
+
]
|
|
323
340
|
for url in urls:
|
|
324
|
-
response = self.client.get(url
|
|
325
|
-
self.assertHttpStatus(response,
|
|
326
|
-
redirect_chain = [(f"/login/?next={url}", 302)]
|
|
327
|
-
self.assertEqual(response.redirect_chain, redirect_chain)
|
|
328
|
-
response_content = response.content.decode(response.charset).replace("\n", "")
|
|
329
|
-
# Assert Footer items(`self.footer_elements`), Banner and Banner Top is hidden
|
|
330
|
-
for footer_text in self.footer_elements:
|
|
331
|
-
self.assertNotIn(footer_text, response_content)
|
|
332
|
-
# Only API Docs implements BANNERS
|
|
333
|
-
if url == urls[0]:
|
|
334
|
-
self.assertNotIn("Hello, Banner Top", response_content)
|
|
335
|
-
self.assertNotIn("Hello, Banner Bottom", response_content)
|
|
341
|
+
response = self.client.get(url)
|
|
342
|
+
self.assertHttpStatus(response, 403)
|
|
336
343
|
|
|
337
344
|
|
|
338
345
|
class MetricsViewTestCase(TestCase):
|
nautobot/core/utils/lookup.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"""Utilities for looking up related classes and information."""
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
import re
|
|
4
5
|
|
|
5
6
|
from django.apps import apps
|
|
6
7
|
from django.conf import settings
|
|
7
8
|
from django.contrib.auth.models import Group
|
|
8
9
|
from django.contrib.contenttypes.models import ContentType
|
|
9
10
|
from django.db.models import Model
|
|
11
|
+
from django.urls import get_resolver, URLPattern, URLResolver
|
|
10
12
|
from django.utils.module_loading import import_string
|
|
11
13
|
|
|
12
14
|
|
|
@@ -218,3 +220,125 @@ def get_created_and_last_updated_usernames_for_model(instance):
|
|
|
218
220
|
last_updated_by = last_updated_by_record.user_name
|
|
219
221
|
|
|
220
222
|
return created_by, last_updated_by
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def get_url_patterns(urlconf=None, patterns_list=None, base_path="/"):
|
|
226
|
+
"""
|
|
227
|
+
Recursively yield a list of registered URL patterns.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
urlconf (URLConf): Python module such as `nautobot.core.urls`.
|
|
231
|
+
Default if unspecified is the value of `settings.ROOT_URLCONF`, i.e. the `nautobot.core.urls` module.
|
|
232
|
+
patterns_list (list): Used in recursion. Generally can be omitted on initial call.
|
|
233
|
+
Default if unspecified is the `url_patterns` attribute of the given `urlconf` module.
|
|
234
|
+
base_path (str): String to prepend to all URL patterns yielded.
|
|
235
|
+
Default if unspecified is the string `"/"`.
|
|
236
|
+
|
|
237
|
+
Yields:
|
|
238
|
+
(str): Each URL pattern defined in the given urlconf and its descendants
|
|
239
|
+
|
|
240
|
+
Examples:
|
|
241
|
+
>>> generator = get_url_patterns()
|
|
242
|
+
>>> next(generator)
|
|
243
|
+
'/'
|
|
244
|
+
>>> next(generator)
|
|
245
|
+
'/search/'
|
|
246
|
+
>>> next(generator)
|
|
247
|
+
'/login/'
|
|
248
|
+
>>> next(generator)
|
|
249
|
+
'/logout/'
|
|
250
|
+
>>> next(generator)
|
|
251
|
+
'/circuits/circuits/<uuid:pk>/terminations/swap/'
|
|
252
|
+
|
|
253
|
+
>>> import example_plugin.urls as example_urls
|
|
254
|
+
>>> for url_pattern in get_url_patterns(example_urls, base_path="/plugins/example-plugin/"):
|
|
255
|
+
... print(url_pattern)
|
|
256
|
+
...
|
|
257
|
+
/plugins/example-plugin/
|
|
258
|
+
/plugins/example-plugin/config/
|
|
259
|
+
/plugins/example-plugin/models/<uuid:pk>/dynamic-groups/
|
|
260
|
+
/plugins/example-plugin/other-models/<uuid:pk>/dynamic-groups/
|
|
261
|
+
/plugins/example-plugin/docs/
|
|
262
|
+
/plugins/example-plugin/circuits/<uuid:pk>/example-plugin-tab/
|
|
263
|
+
/plugins/example-plugin/devices/<uuid:pk>/example-plugin-tab-1/
|
|
264
|
+
/plugins/example-plugin/devices/<uuid:pk>/example-plugin-tab-2/
|
|
265
|
+
/plugins/example-plugin/override-target/
|
|
266
|
+
/plugins/example-plugin/^models/$
|
|
267
|
+
/plugins/example-plugin/^models/add/$
|
|
268
|
+
/plugins/example-plugin/^models/import/$
|
|
269
|
+
/plugins/example-plugin/^models/edit/$
|
|
270
|
+
/plugins/example-plugin/^models/delete/$
|
|
271
|
+
/plugins/example-plugin/^models/all-names/$
|
|
272
|
+
/plugins/example-plugin/^models/(?P<pk>[^/.]+)/$
|
|
273
|
+
/plugins/example-plugin/^models/(?P<pk>[^/.]+)/delete/$
|
|
274
|
+
/plugins/example-plugin/^models/(?P<pk>[^/.]+)/edit/$
|
|
275
|
+
/plugins/example-plugin/^models/(?P<pk>[^/.]+)/changelog/$
|
|
276
|
+
/plugins/example-plugin/^models/(?P<pk>[^/.]+)/notes/$
|
|
277
|
+
/plugins/example-plugin/^other-models/$
|
|
278
|
+
/plugins/example-plugin/^other-models/add/$
|
|
279
|
+
/plugins/example-plugin/^other-models/edit/$
|
|
280
|
+
/plugins/example-plugin/^other-models/delete/$
|
|
281
|
+
/plugins/example-plugin/^other-models/(?P<pk>[^/.]+)/$
|
|
282
|
+
/plugins/example-plugin/^other-models/(?P<pk>[^/.]+)/delete/$
|
|
283
|
+
/plugins/example-plugin/^other-models/(?P<pk>[^/.]+)/edit/$
|
|
284
|
+
/plugins/example-plugin/^other-models/(?P<pk>[^/.]+)/changelog/$
|
|
285
|
+
/plugins/example-plugin/^other-models/(?P<pk>[^/.]+)/notes/$
|
|
286
|
+
"""
|
|
287
|
+
if urlconf is None:
|
|
288
|
+
urlconf = settings.ROOT_URLCONF
|
|
289
|
+
if patterns_list is None:
|
|
290
|
+
patterns_list = get_resolver(urlconf).url_patterns
|
|
291
|
+
|
|
292
|
+
for item in patterns_list:
|
|
293
|
+
if isinstance(item, URLPattern):
|
|
294
|
+
yield base_path + str(item.pattern)
|
|
295
|
+
elif isinstance(item, URLResolver):
|
|
296
|
+
# Recurse!
|
|
297
|
+
yield from get_url_patterns(urlconf, item.url_patterns, base_path + str(item.pattern))
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def get_url_for_url_pattern(url_pattern):
|
|
301
|
+
"""
|
|
302
|
+
Given a URL pattern, construct a URL string that would match that pattern.
|
|
303
|
+
|
|
304
|
+
Examples:
|
|
305
|
+
>>> get_url_for_url_pattern("/plugins/example-plugin/^models/(?P<pk>[^/.]+)/$")
|
|
306
|
+
'/plugins/example-plugin/models/00000000-0000-0000-0000-000000000000/'
|
|
307
|
+
>>> get_url_for_url_pattern("/circuits/circuit-terminations/<uuid:termination_a_id>/connect/<str:termination_b_type>/")
|
|
308
|
+
'/circuits/circuit-terminations/00000000-0000-0000-0000-000000000000/connect/string/'
|
|
309
|
+
"""
|
|
310
|
+
url = url_pattern
|
|
311
|
+
# Fixup tokens in path-style "classic" view URLs:
|
|
312
|
+
# "/admin/users/user/<id>/password/"
|
|
313
|
+
url = re.sub(r"<id>", "00000000-0000-0000-0000-000000000000", url)
|
|
314
|
+
# "/silk/request/<uuid:request_id>/profile/<int:profile_id>/"
|
|
315
|
+
url = re.sub(r"<int:\w+>", "1", url)
|
|
316
|
+
# "/admin/admin/logentry/<path:object_id>/"
|
|
317
|
+
url = re.sub(r"<path:\w+>", "1", url)
|
|
318
|
+
# "/dcim/sites/<slug:slug>/"
|
|
319
|
+
url = re.sub(r"<slug:\w+>", "slug", url)
|
|
320
|
+
# "/apps/installed-apps/<str:app>/"
|
|
321
|
+
url = re.sub(r"<str:\w+>", "string", url)
|
|
322
|
+
# "/dcim/locations/<uuid:pk>/"
|
|
323
|
+
url = re.sub(r"<uuid:\w+>", "00000000-0000-0000-0000-000000000000", url)
|
|
324
|
+
# tokens in regexp-style router urls, including REST and NautobotUIViewSet:
|
|
325
|
+
# "/extras/^external-integrations/(?P<pk>[^/.]+)/$"
|
|
326
|
+
# "/api/virtualization/^interfaces/(?P<pk>[^/.]+)/$"
|
|
327
|
+
# "/api/virtualization/^interfaces/(?P<pk>[^/.]+)\\.(?P<format>[a-z0-9]+)/?$"
|
|
328
|
+
url = re.sub(r"[$^]", "", url)
|
|
329
|
+
url = re.sub(r"/\?", "/", url)
|
|
330
|
+
url = re.sub(r"\(\?P<app_label>[^)]+\)", "users", url)
|
|
331
|
+
url = re.sub(r"\(\?P<class_path>[^)]+\)", "foo/bar/baz", url)
|
|
332
|
+
url = re.sub(r"\(\?P<format>[^)]+\)", "json", url)
|
|
333
|
+
url = re.sub(r"\(\?P<name>[^)]+\)", "string", url)
|
|
334
|
+
url = re.sub(r"\(\?P<pk>[^)]+\)", "00000000-0000-0000-0000-000000000000", url)
|
|
335
|
+
url = re.sub(r"\(\?P<slug>[^)]+\)", "string", url)
|
|
336
|
+
url = re.sub(r"\(\?P<url>[^)]+\)", "any", url)
|
|
337
|
+
# Fallthru for generic URL parameters
|
|
338
|
+
url = re.sub(r"\(\?P<\w+>[^)]+\)\??", "unknown", url)
|
|
339
|
+
url = re.sub(r"\\", "", url)
|
|
340
|
+
|
|
341
|
+
if any(char in url for char in "<>[]()?+^$"):
|
|
342
|
+
raise RuntimeError(f"Unhandled token in URL {url} derived from {url_pattern}")
|
|
343
|
+
|
|
344
|
+
return url
|
nautobot/core/views/__init__.py
CHANGED
|
@@ -11,7 +11,7 @@ from django.contrib.auth.decorators import permission_required
|
|
|
11
11
|
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
|
|
12
12
|
from django.contrib.contenttypes.models import ContentType
|
|
13
13
|
from django.http import HttpResponseForbidden, HttpResponseServerError, JsonResponse
|
|
14
|
-
from django.shortcuts import get_object_or_404,
|
|
14
|
+
from django.shortcuts import get_object_or_404, render
|
|
15
15
|
from django.template import loader, RequestContext, Template
|
|
16
16
|
from django.template.exceptions import TemplateDoesNotExist
|
|
17
17
|
from django.urls import resolve, reverse
|
|
@@ -210,7 +210,7 @@ class SearchView(AccessMixin, View):
|
|
|
210
210
|
)
|
|
211
211
|
|
|
212
212
|
|
|
213
|
-
class StaticMediaFailureView(View):
|
|
213
|
+
class StaticMediaFailureView(View): # NOT using LoginRequiredMixin here as this may happen even on the login page
|
|
214
214
|
"""
|
|
215
215
|
Display a user-friendly error message with troubleshooting tips when a static media file fails to load.
|
|
216
216
|
"""
|
|
@@ -265,12 +265,8 @@ def csrf_failure(request, reason="", template_name="403_csrf_failure.html"):
|
|
|
265
265
|
return HttpResponseForbidden(t.render(context), content_type="text/html")
|
|
266
266
|
|
|
267
267
|
|
|
268
|
-
class CustomGraphQLView(GraphQLView):
|
|
268
|
+
class CustomGraphQLView(LoginRequiredMixin, GraphQLView):
|
|
269
269
|
def render_graphiql(self, request, **data):
|
|
270
|
-
if not request.user.is_authenticated:
|
|
271
|
-
graphql_url = reverse("graphql")
|
|
272
|
-
login_url = reverse(settings.LOGIN_URL)
|
|
273
|
-
return redirect(f"{login_url}?next={graphql_url}")
|
|
274
270
|
query_name = request.GET.get("name")
|
|
275
271
|
if query_name:
|
|
276
272
|
data["obj"] = GraphQLQuery.objects.get(name=query_name)
|
nautobot/core/views/generic.py
CHANGED
|
@@ -4,6 +4,7 @@ import re
|
|
|
4
4
|
|
|
5
5
|
from django.conf import settings
|
|
6
6
|
from django.contrib import messages
|
|
7
|
+
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
7
8
|
from django.contrib.contenttypes.models import ContentType
|
|
8
9
|
from django.core.exceptions import (
|
|
9
10
|
FieldDoesNotExist,
|
|
@@ -57,6 +58,14 @@ from nautobot.extras.models import ExportTemplate
|
|
|
57
58
|
from nautobot.extras.utils import remove_prefix_from_cf_key
|
|
58
59
|
|
|
59
60
|
|
|
61
|
+
class GenericView(LoginRequiredMixin, View):
|
|
62
|
+
"""
|
|
63
|
+
Base class for non-object-related views.
|
|
64
|
+
|
|
65
|
+
Enforces authentication, which Django's base View does not by default.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
|
|
60
69
|
class ObjectView(ObjectPermissionRequiredMixin, View):
|
|
61
70
|
"""
|
|
62
71
|
Retrieve a single object for display.
|
nautobot/dcim/api/urls.py
CHANGED
|
@@ -2,8 +2,7 @@ from nautobot.core.api.routers import OrderedDefaultRouter
|
|
|
2
2
|
|
|
3
3
|
from . import views
|
|
4
4
|
|
|
5
|
-
router = OrderedDefaultRouter()
|
|
6
|
-
router.APIRootView = views.DCIMRootView
|
|
5
|
+
router = OrderedDefaultRouter(view_name="DCIM")
|
|
7
6
|
|
|
8
7
|
# Locations
|
|
9
8
|
router.register("location-types", views.LocationTypeViewSet)
|
nautobot/dcim/api/views.py
CHANGED
|
@@ -13,7 +13,6 @@ from rest_framework.decorators import action
|
|
|
13
13
|
from rest_framework.mixins import ListModelMixin
|
|
14
14
|
from rest_framework.permissions import IsAuthenticated
|
|
15
15
|
from rest_framework.response import Response
|
|
16
|
-
from rest_framework.routers import APIRootView
|
|
17
16
|
from rest_framework.viewsets import GenericViewSet, ViewSet
|
|
18
17
|
|
|
19
18
|
from nautobot.circuits.models import Circuit
|
|
@@ -69,16 +68,6 @@ from nautobot.virtualization.models import VirtualMachine
|
|
|
69
68
|
from . import serializers
|
|
70
69
|
from .exceptions import MissingFilterException
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
class DCIMRootView(APIRootView):
|
|
74
|
-
"""
|
|
75
|
-
DCIM API root view
|
|
76
|
-
"""
|
|
77
|
-
|
|
78
|
-
def get_view_name(self):
|
|
79
|
-
return "DCIM"
|
|
80
|
-
|
|
81
|
-
|
|
82
71
|
# Mixins
|
|
83
72
|
|
|
84
73
|
|
|
@@ -762,7 +751,7 @@ class ConnectedDeviceViewSet(ViewSet):
|
|
|
762
751
|
|
|
763
752
|
# Determine local interface from peer interface's connection
|
|
764
753
|
peer_interface = get_object_or_404(
|
|
765
|
-
Interface.objects.
|
|
754
|
+
Interface.objects.restrict(request.user, "view"),
|
|
766
755
|
device__name=peer_device_name,
|
|
767
756
|
name=peer_interface_name,
|
|
768
757
|
)
|
nautobot/dcim/choices.py
CHANGED
|
@@ -222,6 +222,10 @@ class PowerPortTypeChoices(ChoiceSet):
|
|
|
222
222
|
TYPE_IEC_3PNE4H = "iec-60309-3p-n-e-4h"
|
|
223
223
|
TYPE_IEC_3PNE6H = "iec-60309-3p-n-e-6h"
|
|
224
224
|
TYPE_IEC_3PNE9H = "iec-60309-3p-n-e-9h"
|
|
225
|
+
# IEC 60906-1
|
|
226
|
+
TYPE_IEC_60906_1 = "iec-60906-1"
|
|
227
|
+
TYPE_NBR_14136_10A = "nbr-14136-10a"
|
|
228
|
+
TYPE_NBR_14136_20A = "nbr-14136-20a"
|
|
225
229
|
# NEMA non-locking
|
|
226
230
|
TYPE_NEMA_115P = "nema-1-15p"
|
|
227
231
|
TYPE_NEMA_515P = "nema-5-15p"
|
|
@@ -339,6 +343,14 @@ class PowerPortTypeChoices(ChoiceSet):
|
|
|
339
343
|
(TYPE_IEC_3PNE9H, "3P+N+E 9H"),
|
|
340
344
|
),
|
|
341
345
|
),
|
|
346
|
+
(
|
|
347
|
+
"IEC 60906-1",
|
|
348
|
+
(
|
|
349
|
+
(TYPE_IEC_60906_1, "IEC 60906-1"),
|
|
350
|
+
(TYPE_NBR_14136_10A, "2P+T 10A (NBR 14136)"),
|
|
351
|
+
(TYPE_NBR_14136_20A, "2P+T 20A (NBR 14136)"),
|
|
352
|
+
),
|
|
353
|
+
),
|
|
342
354
|
(
|
|
343
355
|
"NEMA (Non-locking)",
|
|
344
356
|
(
|
|
@@ -482,6 +494,10 @@ class PowerOutletTypeChoices(ChoiceSet):
|
|
|
482
494
|
TYPE_IEC_3PNE4H = "iec-60309-3p-n-e-4h"
|
|
483
495
|
TYPE_IEC_3PNE6H = "iec-60309-3p-n-e-6h"
|
|
484
496
|
TYPE_IEC_3PNE9H = "iec-60309-3p-n-e-9h"
|
|
497
|
+
# IEC 60906-1
|
|
498
|
+
TYPE_IEC_60906_1 = "iec-60906-1"
|
|
499
|
+
TYPE_NBR_14136_10A = "nbr-14136-10a"
|
|
500
|
+
TYPE_NBR_14136_20A = "nbr-14136-20a"
|
|
485
501
|
# NEMA non-locking
|
|
486
502
|
TYPE_NEMA_115R = "nema-1-15r"
|
|
487
503
|
TYPE_NEMA_515R = "nema-5-15r"
|
|
@@ -592,6 +608,14 @@ class PowerOutletTypeChoices(ChoiceSet):
|
|
|
592
608
|
(TYPE_IEC_3PNE9H, "3P+N+E 9H"),
|
|
593
609
|
),
|
|
594
610
|
),
|
|
611
|
+
(
|
|
612
|
+
"IEC 60906-1",
|
|
613
|
+
(
|
|
614
|
+
(TYPE_IEC_60906_1, "IEC 60906-1"),
|
|
615
|
+
(TYPE_NBR_14136_10A, "2P+T 10A (NBR 14136)"),
|
|
616
|
+
(TYPE_NBR_14136_20A, "2P+T 20A (NBR 14136)"),
|
|
617
|
+
),
|
|
618
|
+
),
|
|
595
619
|
(
|
|
596
620
|
"NEMA (Non-locking)",
|
|
597
621
|
(
|
|
@@ -750,11 +774,20 @@ class InterfaceTypeChoices(ChoiceSet):
|
|
|
750
774
|
TYPE_100GE_CFP4 = "100gbase-x-cfp4"
|
|
751
775
|
TYPE_100GE_CPAK = "100gbase-x-cpak"
|
|
752
776
|
TYPE_100GE_QSFP28 = "100gbase-x-qsfp28"
|
|
777
|
+
TYPE_100GE_CXP = "100gbase-x-cxp"
|
|
778
|
+
TYPE_100GE_DSFP = "100gbase-x-dsfp"
|
|
779
|
+
TYPE_100GE_SFP_DD = "100gbase-x-sfpdd"
|
|
780
|
+
TYPE_100GE_QSFP_DD = "100gbase-x-qsfpdd"
|
|
753
781
|
TYPE_200GE_CFP2 = "200gbase-x-cfp2"
|
|
754
782
|
TYPE_200GE_QSFP56 = "200gbase-x-qsfp56"
|
|
783
|
+
TYPE_200GE_QSFP_DD = "200gbase-x-qsfpdd"
|
|
755
784
|
TYPE_400GE_QSFP112 = "400gbase-x-qsfp112"
|
|
756
785
|
TYPE_400GE_QSFP_DD = "400gbase-x-qsfpdd"
|
|
757
786
|
TYPE_400GE_OSFP = "400gbase-x-osfp"
|
|
787
|
+
TYPE_400GE_CFP2 = "400gbase-x-cfp2"
|
|
788
|
+
TYPE_400GE_OSFP_RHS = "400gbase-x-osfp-rhs"
|
|
789
|
+
TYPE_400GE_CDFP = "400gbase-x-cdfp"
|
|
790
|
+
TYPE_400GE_CFP8 = "400gbase-x-cfp8"
|
|
758
791
|
TYPE_800GE_QSFP_DD = "800gbase-x-qsfpdd"
|
|
759
792
|
TYPE_800GE_OSFP = "800gbase-x-osfp"
|
|
760
793
|
|
|
@@ -801,7 +834,10 @@ class InterfaceTypeChoices(ChoiceSet):
|
|
|
801
834
|
TYPE_8GFC_SFP_PLUS = "8gfc-sfpp"
|
|
802
835
|
TYPE_16GFC_SFP_PLUS = "16gfc-sfpp"
|
|
803
836
|
TYPE_32GFC_SFP28 = "32gfc-sfp28"
|
|
837
|
+
TYPE_32GFC_SFP_PLUS = "32gfc-sfpp"
|
|
804
838
|
TYPE_64GFC_QSFP_PLUS = "64gfc-qsfpp"
|
|
839
|
+
TYPE_64GFC_SFP_DD = "64gfc-sfpdd"
|
|
840
|
+
TYPE_64GFC_SFP_PLUS = "64gfc-sfpp"
|
|
805
841
|
TYPE_128GFC_QSFP28 = "128gfc-sfp28"
|
|
806
842
|
|
|
807
843
|
# InfiniBand
|
|
@@ -909,13 +945,22 @@ class InterfaceTypeChoices(ChoiceSet):
|
|
|
909
945
|
(TYPE_100GE_CFP, "CFP (100GE)"),
|
|
910
946
|
(TYPE_100GE_CFP2, "CFP2 (100GE)"),
|
|
911
947
|
(TYPE_200GE_CFP2, "CFP2 (200GE)"),
|
|
948
|
+
(TYPE_400GE_CFP2, "CFP2 (400GE)"),
|
|
912
949
|
(TYPE_100GE_CFP4, "CFP4 (100GE)"),
|
|
913
950
|
(TYPE_100GE_CPAK, "Cisco CPAK (100GE)"),
|
|
914
951
|
(TYPE_100GE_QSFP28, "QSFP28 (100GE)"),
|
|
952
|
+
(TYPE_100GE_CXP, "CXP (100GE)"),
|
|
953
|
+
(TYPE_100GE_QSFP_DD, "QSFP-DD (100GE)"),
|
|
954
|
+
(TYPE_100GE_DSFP, "DSFP (100GE)"),
|
|
955
|
+
(TYPE_100GE_SFP_DD, "SFP-DD (100GE)"),
|
|
915
956
|
(TYPE_200GE_QSFP56, "QSFP56 (200GE)"),
|
|
957
|
+
(TYPE_200GE_QSFP_DD, "QSFP-DD (200GE)"),
|
|
916
958
|
(TYPE_400GE_QSFP112, "QSFP112 (400GE)"),
|
|
917
959
|
(TYPE_400GE_QSFP_DD, "QSFP-DD (400GE)"),
|
|
918
960
|
(TYPE_400GE_OSFP, "OSFP (400GE)"),
|
|
961
|
+
(TYPE_400GE_OSFP_RHS, "OSFP-RHS (400GE)"),
|
|
962
|
+
(TYPE_400GE_CDFP, "CDFP (400GE)"),
|
|
963
|
+
(TYPE_400GE_CFP8, "CPF8 (400GE)"),
|
|
919
964
|
(TYPE_800GE_QSFP_DD, "QSFP-DD (800GE)"),
|
|
920
965
|
(TYPE_800GE_OSFP, "OSFP (800GE)"),
|
|
921
966
|
),
|
|
@@ -977,7 +1022,10 @@ class InterfaceTypeChoices(ChoiceSet):
|
|
|
977
1022
|
(TYPE_8GFC_SFP_PLUS, "SFP+ (8GFC)"),
|
|
978
1023
|
(TYPE_16GFC_SFP_PLUS, "SFP+ (16GFC)"),
|
|
979
1024
|
(TYPE_32GFC_SFP28, "SFP28 (32GFC)"),
|
|
1025
|
+
(TYPE_32GFC_SFP_PLUS, "SFP+ (32GFC)"),
|
|
980
1026
|
(TYPE_64GFC_QSFP_PLUS, "QSFP+ (64GFC)"),
|
|
1027
|
+
(TYPE_64GFC_SFP_DD, "SFP-DD (64GFC)"),
|
|
1028
|
+
(TYPE_64GFC_SFP_PLUS, "SFP+ (64GFC)"),
|
|
981
1029
|
(TYPE_128GFC_QSFP28, "QSFP28 (128GFC)"),
|
|
982
1030
|
),
|
|
983
1031
|
),
|
|
@@ -1124,6 +1172,10 @@ class PortTypeChoices(ChoiceSet):
|
|
|
1124
1172
|
TYPE_LSH_PC = "lsh-pc"
|
|
1125
1173
|
TYPE_LSH_UPC = "lsh-upc"
|
|
1126
1174
|
TYPE_LSH_APC = "lsh-apc"
|
|
1175
|
+
TYPE_LX5 = "lx5"
|
|
1176
|
+
TYPE_LX5_PC = "lx5-pc"
|
|
1177
|
+
TYPE_LX5_UPC = "lx5-upc"
|
|
1178
|
+
TYPE_LX5_APC = "lx5-apc"
|
|
1127
1179
|
TYPE_SPLICE = "splice"
|
|
1128
1180
|
TYPE_CS = "cs"
|
|
1129
1181
|
TYPE_SN = "sn"
|
|
@@ -1170,6 +1222,10 @@ class PortTypeChoices(ChoiceSet):
|
|
|
1170
1222
|
(TYPE_LSH_PC, "LSH/PC"),
|
|
1171
1223
|
(TYPE_LSH_UPC, "LSH/UPC"),
|
|
1172
1224
|
(TYPE_LSH_APC, "LSH/APC"),
|
|
1225
|
+
(TYPE_LX5, "LX.5"),
|
|
1226
|
+
(TYPE_LX5_PC, "LX.5/PC"),
|
|
1227
|
+
(TYPE_LX5_UPC, "LX.5/UPC"),
|
|
1228
|
+
(TYPE_LX5_APC, "LX.5/APC"),
|
|
1173
1229
|
(TYPE_MPO, "MPO"),
|
|
1174
1230
|
(TYPE_MTRJ, "MTRJ"),
|
|
1175
1231
|
(TYPE_SC, "SC"),
|
nautobot/dcim/models/racks.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from collections import OrderedDict
|
|
2
|
-
|
|
3
1
|
from django.conf import settings
|
|
4
2
|
from django.contrib.contenttypes.fields import GenericRelation
|
|
5
3
|
from django.contrib.contenttypes.models import ContentType
|
|
@@ -274,7 +272,7 @@ class Rack(PrimaryModel):
|
|
|
274
272
|
contains a height attribute for the device
|
|
275
273
|
"""
|
|
276
274
|
|
|
277
|
-
elevation =
|
|
275
|
+
elevation = {}
|
|
278
276
|
for u in self.units:
|
|
279
277
|
elevation[u] = {
|
|
280
278
|
"id": u,
|
nautobot/dcim/navigation.py
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<th>Interface</th>
|
|
16
16
|
<th>Configured Device</th>
|
|
17
17
|
<th>Configured Interface</th>
|
|
18
|
+
<th>Configured MAC Address</th>
|
|
18
19
|
<th>LLDP Device</th>
|
|
19
20
|
<th>LLDP Interface</th>
|
|
20
21
|
</tr>
|
|
@@ -27,18 +28,21 @@
|
|
|
27
28
|
<td class="configured_device" data="{{ iface.connected_endpoint.device }}" data-chassis="{{ iface.connected_endpoint.device.virtual_chassis.name }}">
|
|
28
29
|
{{ iface.connected_endpoint.device|hyperlinked_object }}
|
|
29
30
|
</td>
|
|
30
|
-
<td class="configured_interface" data="{{ iface.connected_endpoint }}">
|
|
31
|
+
<td class="configured_interface" data-interface-name="{{ iface.connected_endpoint }}">
|
|
31
32
|
<span title="{{ iface.connected_endpoint.get_type_display }}">{{ iface.connected_endpoint }}</span>
|
|
32
33
|
</td>
|
|
34
|
+
<td class="configured_mac" data-mac-address="{{ iface.connected_endpoint.mac_address }}">
|
|
35
|
+
<span>{{ iface.connected_endpoint.mac_address }}</span>
|
|
36
|
+
</td>
|
|
33
37
|
{% elif iface.connected_endpoint.circuit %}
|
|
34
38
|
{% with circuit=iface.connected_endpoint.circuit %}
|
|
35
|
-
<td colspan="
|
|
39
|
+
<td colspan="3">
|
|
36
40
|
<i class="mdi mdi-lightning-bolt" title="Circuit"></i>
|
|
37
41
|
<a href="{{ circuit.get_absolute_url }}">{{ circuit.provider }} {{ circuit }}</a>
|
|
38
42
|
</td>
|
|
39
43
|
{% endwith %}
|
|
40
44
|
{% else %}
|
|
41
|
-
<td colspan="
|
|
45
|
+
<td colspan="3">None</td>
|
|
42
46
|
{% endif %}
|
|
43
47
|
<td class="device"></td>
|
|
44
48
|
<td class="interface"></td>
|
|
@@ -52,51 +56,71 @@
|
|
|
52
56
|
{% block javascript %}
|
|
53
57
|
{{ block.super }}
|
|
54
58
|
<script type="text/javascript">
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
var ready = (callback) => {
|
|
60
|
+
if (document.readyState != "loading") {
|
|
61
|
+
callback();
|
|
62
|
+
} else {
|
|
63
|
+
document.addEventListener("DOMContentLoaded", callback);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
ready(() => {
|
|
68
|
+
fetch("{% url 'dcim-api:device-napalm' pk=object.pk %}?method=get_lldp_neighbors_detail")
|
|
69
|
+
.then((response) => {
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw Error(response.statusText);
|
|
72
|
+
}
|
|
73
|
+
return response.json();
|
|
74
|
+
})
|
|
75
|
+
.then((data) => {
|
|
76
|
+
const interfaces = data["get_lldp_neighbors_detail"];
|
|
77
|
+
for (var iface of Object.keys(interfaces)) {
|
|
78
|
+
const neighbor = interfaces[iface][0];
|
|
79
|
+
const row = document.querySelector('*[data-interface-name="'+ iface.split(".")[0].replace(/([\/:])/g, "\\$1") + '"]');
|
|
80
|
+
// var row = $('*[data-interface-name="' + iface.split(".")[0].replace(/([\/:])/g, "\\$1") + '"]');
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
// Glean configured hostnames/interfaces from the DOM
|
|
83
|
+
const configured_device = row.querySelector('td.configured_device').getAttribute('data');
|
|
84
|
+
const configured_chassis = row.querySelector('td.configured_device').getAttribute('data-chassis');
|
|
85
|
+
const configured_interface = row.querySelector('td.configured_interface').getAttribute('data-interface-name').toLowerCase();
|
|
86
|
+
const configured_mac_address = row.querySelector('td.configured_mac').getAttribute('data-mac-address').toLowerCase();
|
|
87
|
+
let configured_interface_short = null;
|
|
88
|
+
if (configured_interface) {
|
|
89
|
+
// Match long-form IOS names against short ones (e.g. Gi0/1 == GigabitEthernet0/1).
|
|
90
|
+
configured_interface_short = configured_interface.replace(/^([A-Z][a-z])[^0-9]*([0-9\/]+)$/, "$1$2");
|
|
91
|
+
}
|
|
73
92
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
// Clean up hostnames/interfaces learned via LLDP
|
|
94
|
+
const neighbor_host = neighbor['remote_system_name'] || ""; // sanitize hostname if it's null to avoid breaking the split func
|
|
95
|
+
const neighbor_port = neighbor['remote_port'] || ""; // sanitize port if it's null to avoid breaking the split func
|
|
96
|
+
const lldp_device = neighbor_host.split(".")[0]; // Strip off any trailing domain name
|
|
97
|
+
const lldp_interface = neighbor_port.split(".")[0].toLowerCase(); // Strip off any trailing subinterface ID
|
|
79
98
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
99
|
+
// Add LLDP neighbors to table
|
|
100
|
+
row.querySelector('td.device').textContent = lldp_device;
|
|
101
|
+
row.querySelector('td.interface').textContent = lldp_interface;
|
|
83
102
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
// Apply colors to rows
|
|
104
|
+
if (!configured_device && lldp_device) {
|
|
105
|
+
row.classList.add('info');
|
|
106
|
+
} else if ((configured_device == lldp_device || configured_chassis == lldp_device) && configured_interface == lldp_interface) {
|
|
107
|
+
row.classList.add('success');
|
|
108
|
+
} else if ((configured_device == lldp_device || configured_chassis == lldp_device) && configured_interface_short == lldp_interface) {
|
|
109
|
+
row.classList.add('success');
|
|
110
|
+
} else if ((configured_device == lldp_device || configured_chassis == lldp_device) && configured_mac_address == lldp_interface) {
|
|
111
|
+
row.classList.add('success');
|
|
112
|
+
} else {
|
|
113
|
+
row.classList.add('danger');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
.catch((error) => {
|
|
118
|
+
if (error.responseText) {
|
|
119
|
+
alert(error.responseText);
|
|
120
|
+
} else {
|
|
121
|
+
throw error;
|
|
98
122
|
}
|
|
99
123
|
});
|
|
100
124
|
});
|
|
101
125
|
</script>
|
|
102
|
-
{% endblock %}
|
|
126
|
+
{% endblock %}
|
nautobot/dcim/tests/test_api.py
CHANGED
|
@@ -2092,7 +2092,10 @@ class ConnectedDeviceTest(APITestCase):
|
|
|
2092
2092
|
def test_get_connected_device(self):
|
|
2093
2093
|
url = reverse("dcim-api:connected-device-list")
|
|
2094
2094
|
response = self.client.get(url + "?peer_device=TestDevice2&peer_interface=eth0", **self.header)
|
|
2095
|
+
self.assertHttpStatus(response, status.HTTP_404_NOT_FOUND)
|
|
2095
2096
|
|
|
2097
|
+
self.add_permissions("dcim.view_interface")
|
|
2098
|
+
response = self.client.get(url + "?peer_device=TestDevice2&peer_interface=eth0", **self.header)
|
|
2096
2099
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
2097
2100
|
self.assertEqual(response.data["name"], self.device1.name)
|
|
2098
2101
|
|