nautobot 2.1.8__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/core/api/routers.py +25 -3
- nautobot/core/api/utils.py +4 -0
- nautobot/core/api/views.py +21 -15
- nautobot/core/settings.py +1 -0
- nautobot/core/templates/admin/base.html +23 -94
- 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/rest_framework/api.html +12 -5
- nautobot/core/tests/integration/test_view_authentication.py +67 -0
- nautobot/core/tests/test_graphql.py +2 -14
- nautobot/core/tests/test_views.py +22 -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/models/racks.py +1 -3
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +67 -43
- nautobot/dcim/tests/test_api.py +3 -0
- nautobot/dcim/views.py +5 -2
- nautobot/extras/api/urls.py +1 -2
- nautobot/extras/api/views.py +0 -10
- nautobot/extras/plugins/views.py +6 -9
- nautobot/extras/tests/test_views.py +101 -0
- nautobot/extras/views.py +10 -10
- nautobot/ipam/api/urls.py +1 -2
- nautobot/ipam/api/views.py +0 -11
- nautobot/ipam/tables.py +0 -1
- nautobot/ipam/tests/test_graphql.py +2 -3
- nautobot/ipam/views.py +9 -9
- nautobot/project-static/css/base.css +1 -0
- nautobot/project-static/docs/404.html +14 -0
- nautobot/project-static/docs/apps/index.html +14 -0
- nautobot/project-static/docs/apps/nautobot-apps.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +394 -408
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +14 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +649 -183
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1811 -1744
- nautobot/project-static/docs/development/apps/api/configuration-view.html +14 -0
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +14 -0
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +14 -0
- nautobot/project-static/docs/development/apps/api/models/global-search.html +14 -0
- nautobot/project-static/docs/development/apps/api/models/graphql.html +14 -0
- nautobot/project-static/docs/development/apps/api/models/index.html +14 -0
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +14 -0
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +14 -0
- nautobot/project-static/docs/development/apps/api/prometheus.html +14 -0
- nautobot/project-static/docs/development/apps/api/setup.html +14 -0
- nautobot/project-static/docs/development/apps/api/testing.html +14 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +14 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +14 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +14 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +14 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/base-template.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +28 -9
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +31 -12
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/index.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/notes.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +14 -0
- nautobot/project-static/docs/development/apps/api/views/urls.html +14 -0
- nautobot/project-static/docs/development/apps/index.html +14 -0
- nautobot/project-static/docs/development/apps/migration/code-updates.html +14 -0
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +14 -0
- nautobot/project-static/docs/development/apps/migration/from-v1.html +14 -0
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +14 -0
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +14 -0
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +14 -0
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +14 -0
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +14 -0
- nautobot/project-static/docs/development/core/application-registry.html +14 -0
- nautobot/project-static/docs/development/core/best-practices.html +14 -0
- nautobot/project-static/docs/development/core/bootstrap-ui.html +14 -0
- nautobot/project-static/docs/development/core/caching.html +14 -0
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +14 -0
- nautobot/project-static/docs/development/core/extending-models.html +14 -0
- nautobot/project-static/docs/development/core/generic-views.html +14 -0
- nautobot/project-static/docs/development/core/getting-started.html +33 -10
- nautobot/project-static/docs/development/core/homepage.html +14 -0
- nautobot/project-static/docs/development/core/index.html +14 -0
- nautobot/project-static/docs/development/core/model-features.html +14 -0
- nautobot/project-static/docs/development/core/natural-keys.html +14 -0
- nautobot/project-static/docs/development/core/navigation-menu.html +14 -0
- nautobot/project-static/docs/development/core/release-checklist.html +14 -0
- nautobot/project-static/docs/development/core/role-internals.html +14 -0
- nautobot/project-static/docs/development/core/style-guide.html +14 -0
- nautobot/project-static/docs/development/core/templates.html +14 -0
- nautobot/project-static/docs/development/core/testing.html +14 -0
- nautobot/project-static/docs/development/core/user-preferences.html +14 -0
- nautobot/project-static/docs/development/index.html +14 -0
- nautobot/project-static/docs/development/jobs/index.html +14 -0
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +14 -0
- nautobot/project-static/docs/index.html +14 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/release-notes/index.html +14 -0
- nautobot/project-static/docs/release-notes/version-1.0.html +14 -0
- nautobot/project-static/docs/release-notes/version-1.1.html +14 -0
- nautobot/project-static/docs/release-notes/version-1.2.html +14 -0
- nautobot/project-static/docs/release-notes/version-1.3.html +14 -0
- nautobot/project-static/docs/release-notes/version-1.4.html +14 -0
- nautobot/project-static/docs/release-notes/version-1.5.html +14 -0
- nautobot/project-static/docs/release-notes/version-1.6.html +14 -0
- nautobot/project-static/docs/release-notes/version-2.0.html +14 -0
- nautobot/project-static/docs/release-notes/version-2.1.html +365 -159
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +245 -240
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +14 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +14 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +14 -0
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +14 -0
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +14 -0
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +14 -0
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +14 -0
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +14 -0
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +14 -0
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +14 -0
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +14 -0
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +14 -0
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +14 -0
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +14 -0
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +14 -0
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +14 -0
- nautobot/project-static/docs/user-guide/administration/installation/docker.html +21 -3
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +14 -0
- nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +6019 -0
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +14 -0
- nautobot/project-static/docs/user-guide/administration/installation/index.html +14 -0
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +14 -0
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +14 -0
- nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +17 -3
- nautobot/project-static/docs/user-guide/administration/installation/services.html +14 -0
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +14 -0
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +14 -0
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +14 -0
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +14 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +14 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +14 -0
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +14 -0
- nautobot/project-static/docs/user-guide/index.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +14 -0
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +14 -0
- nautobot/tenancy/api/urls.py +1 -2
- nautobot/tenancy/api/views.py +0 -12
- 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-2.1.8.dist-info → nautobot-2.1.9.dist-info}/METADATA +2 -2
- {nautobot-2.1.8.dist-info → nautobot-2.1.9.dist-info}/RECORD +296 -294
- {nautobot-2.1.8.dist-info → nautobot-2.1.9.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.1.8.dist-info → nautobot-2.1.9.dist-info}/NOTICE +0 -0
- {nautobot-2.1.8.dist-info → nautobot-2.1.9.dist-info}/WHEEL +0 -0
- {nautobot-2.1.8.dist-info → nautobot-2.1.9.dist-info}/entry_points.txt +0 -0
|
@@ -315,25 +315,31 @@ class LoginUI(TestCase):
|
|
|
315
315
|
sso_login_search_result = self.make_request()
|
|
316
316
|
self.assertIsNotNone(sso_login_search_result)
|
|
317
317
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
"""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."""
|
|
321
320
|
self.client.logout()
|
|
322
321
|
headers = {"HTTP_ACCEPT": "text/html"}
|
|
323
|
-
|
|
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
|
+
]
|
|
324
340
|
for url in urls:
|
|
325
|
-
response = self.client.get(url
|
|
326
|
-
self.assertHttpStatus(response,
|
|
327
|
-
redirect_chain = [(f"/login/?next={url}", 302)]
|
|
328
|
-
self.assertEqual(response.redirect_chain, redirect_chain)
|
|
329
|
-
response_content = response.content.decode(response.charset).replace("\n", "")
|
|
330
|
-
# Assert Footer items(`self.footer_elements`), Banner and Banner Top is hidden
|
|
331
|
-
for footer_text in self.footer_elements:
|
|
332
|
-
self.assertNotIn(footer_text, response_content)
|
|
333
|
-
# Only API Docs implements BANNERS
|
|
334
|
-
if url == urls[0]:
|
|
335
|
-
self.assertNotIn("Hello, Banner Top", response_content)
|
|
336
|
-
self.assertNotIn("Hello, Banner Bottom", response_content)
|
|
341
|
+
response = self.client.get(url)
|
|
342
|
+
self.assertHttpStatus(response, 403)
|
|
337
343
|
|
|
338
344
|
|
|
339
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/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,
|
|
@@ -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
|
|
nautobot/dcim/views.py
CHANGED
|
@@ -11,7 +11,7 @@ from django.forms import (
|
|
|
11
11
|
ModelMultipleChoiceField,
|
|
12
12
|
MultipleHiddenInput,
|
|
13
13
|
)
|
|
14
|
-
from django.shortcuts import get_object_or_404, redirect, render
|
|
14
|
+
from django.shortcuts import get_object_or_404, HttpResponse, redirect, render
|
|
15
15
|
from django.utils.functional import cached_property
|
|
16
16
|
from django.utils.html import format_html
|
|
17
17
|
from django.views.generic import View
|
|
@@ -2316,7 +2316,7 @@ class CableCreateView(generic.ObjectEditView):
|
|
|
2316
2316
|
"rear-port": forms.ConnectCableToRearPortForm,
|
|
2317
2317
|
"power-feed": forms.ConnectCableToPowerFeedForm,
|
|
2318
2318
|
"circuit-termination": forms.ConnectCableToCircuitTerminationForm,
|
|
2319
|
-
}
|
|
2319
|
+
}.get(kwargs.get("termination_b_type"), None)
|
|
2320
2320
|
|
|
2321
2321
|
return super().dispatch(request, *args, **kwargs)
|
|
2322
2322
|
|
|
@@ -2333,6 +2333,9 @@ class CableCreateView(generic.ObjectEditView):
|
|
|
2333
2333
|
return obj
|
|
2334
2334
|
|
|
2335
2335
|
def get(self, request, *args, **kwargs):
|
|
2336
|
+
if self.model_form is None:
|
|
2337
|
+
return HttpResponse(status_code=400)
|
|
2338
|
+
|
|
2336
2339
|
obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
|
|
2337
2340
|
|
|
2338
2341
|
# Parse initial data manually to avoid setting field values as lists
|
nautobot/extras/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.ExtrasRootView
|
|
5
|
+
router = OrderedDefaultRouter(view_name="Extras")
|
|
7
6
|
|
|
8
7
|
# Computed Fields
|
|
9
8
|
router.register("computed-fields", views.ComputedFieldViewSet)
|
nautobot/extras/api/views.py
CHANGED
|
@@ -16,7 +16,6 @@ from rest_framework.exceptions import MethodNotAllowed, PermissionDenied, Valida
|
|
|
16
16
|
from rest_framework.parsers import JSONParser, MultiPartParser
|
|
17
17
|
from rest_framework.permissions import IsAuthenticated
|
|
18
18
|
from rest_framework.response import Response
|
|
19
|
-
from rest_framework.routers import APIRootView
|
|
20
19
|
|
|
21
20
|
from nautobot.core.api.authentication import TokenPermissions
|
|
22
21
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
@@ -74,15 +73,6 @@ from nautobot.extras.utils import get_worker_count
|
|
|
74
73
|
from . import serializers
|
|
75
74
|
|
|
76
75
|
|
|
77
|
-
class ExtrasRootView(APIRootView):
|
|
78
|
-
"""
|
|
79
|
-
Extras API root view
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
def get_view_name(self):
|
|
83
|
-
return "Extras"
|
|
84
|
-
|
|
85
|
-
|
|
86
76
|
class NotesViewSetMixin:
|
|
87
77
|
def restrict_queryset(self, request, *args, **kwargs):
|
|
88
78
|
"""
|
nautobot/extras/plugins/views.py
CHANGED
|
@@ -2,7 +2,6 @@ from collections import OrderedDict
|
|
|
2
2
|
|
|
3
3
|
from django.apps import apps
|
|
4
4
|
from django.conf import settings
|
|
5
|
-
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
6
5
|
from django.http import Http404
|
|
7
6
|
from django.shortcuts import render
|
|
8
7
|
from django.urls.exceptions import NoReverseMatch
|
|
@@ -14,8 +13,9 @@ from rest_framework.response import Response
|
|
|
14
13
|
from rest_framework.reverse import reverse
|
|
15
14
|
from rest_framework.views import APIView
|
|
16
15
|
|
|
17
|
-
from nautobot.core.api.views import NautobotAPIVersionMixin
|
|
16
|
+
from nautobot.core.api.views import AuthenticatedAPIRootView, NautobotAPIVersionMixin
|
|
18
17
|
from nautobot.core.forms import TableConfigForm
|
|
18
|
+
from nautobot.core.views.generic import GenericView
|
|
19
19
|
from nautobot.core.views.mixins import AdminRequiredMixin
|
|
20
20
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
21
21
|
from nautobot.extras.plugins.tables import InstalledPluginsTable
|
|
@@ -67,7 +67,7 @@ class InstalledPluginsView(AdminRequiredMixin, View):
|
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
class InstalledPluginDetailView(
|
|
70
|
+
class InstalledPluginDetailView(GenericView):
|
|
71
71
|
"""
|
|
72
72
|
View for showing details of an installed plugin.
|
|
73
73
|
"""
|
|
@@ -92,7 +92,6 @@ class InstalledPluginsAPIView(NautobotAPIVersionMixin, APIView):
|
|
|
92
92
|
"""
|
|
93
93
|
|
|
94
94
|
permission_classes = [permissions.IsAdminUser]
|
|
95
|
-
_ignore_model_permissions = True
|
|
96
95
|
|
|
97
96
|
def get_view_name(self):
|
|
98
97
|
return "Installed Plugins"
|
|
@@ -128,11 +127,9 @@ class InstalledPluginsAPIView(NautobotAPIVersionMixin, APIView):
|
|
|
128
127
|
return Response([self._get_plugin_data(apps.get_app_config(plugin)) for plugin in settings.PLUGINS])
|
|
129
128
|
|
|
130
129
|
|
|
131
|
-
class PluginsAPIRootView(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def get_view_name(self):
|
|
135
|
-
return "Plugins"
|
|
130
|
+
class PluginsAPIRootView(AuthenticatedAPIRootView):
|
|
131
|
+
name = "Apps"
|
|
132
|
+
description = "API extension point for installed Nautobot Apps"
|
|
136
133
|
|
|
137
134
|
@staticmethod
|
|
138
135
|
def _get_plugin_entry(plugin, app_config, request, format_):
|