nautobot 2.2.0__py3-none-any.whl → 2.2.0b1__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 +2 -1
- nautobot/apps/utils.py +0 -4
- nautobot/apps/views.py +0 -2
- nautobot/circuits/api/urls.py +2 -1
- nautobot/circuits/api/views.py +12 -0
- nautobot/circuits/tests/test_filters.py +1 -1
- nautobot/core/api/routers.py +3 -25
- nautobot/core/api/utils.py +0 -4
- nautobot/core/api/views.py +15 -21
- nautobot/core/filters.py +1 -7
- nautobot/core/management/commands/generate_test_data.py +4 -4
- nautobot/core/settings.py +0 -1
- nautobot/core/tables.py +1 -2
- nautobot/core/templates/admin/base.html +94 -23
- nautobot/core/templates/graphene/graphiql.html +47 -18
- nautobot/core/templates/inc/footer.html +5 -5
- nautobot/core/templates/inc/nav_menu.html +7 -0
- nautobot/core/templates/rest_framework/api.html +5 -12
- nautobot/core/templatetags/helpers.py +2 -2
- nautobot/core/testing/views.py +0 -30
- nautobot/core/tests/test_api.py +6 -13
- nautobot/core/tests/test_csv.py +4 -5
- nautobot/core/tests/test_filters.py +1 -2
- nautobot/core/tests/test_graphql.py +14 -4
- nautobot/core/tests/test_navigations.py +0 -3
- nautobot/core/tests/test_views.py +16 -22
- nautobot/core/utils/lookup.py +0 -124
- nautobot/core/views/__init__.py +7 -3
- nautobot/core/views/generic.py +3 -19
- nautobot/core/views/mixins.py +0 -7
- nautobot/core/views/renderers.py +1 -4
- nautobot/dcim/api/serializers.py +4 -4
- nautobot/dcim/api/urls.py +3 -2
- nautobot/dcim/api/views.py +18 -7
- nautobot/dcim/factory.py +7 -7
- nautobot/dcim/filters/__init__.py +17 -16
- nautobot/dcim/forms.py +45 -61
- nautobot/dcim/homepage.py +3 -11
- nautobot/dcim/migrations/0057_controller_models.py +70 -11
- nautobot/dcim/models/__init__.py +2 -2
- nautobot/dcim/models/devices.py +16 -14
- nautobot/dcim/models/racks.py +3 -1
- nautobot/dcim/navigation.py +31 -23
- nautobot/dcim/signals.py +6 -6
- nautobot/dcim/tables/__init__.py +2 -2
- nautobot/dcim/tables/devices.py +15 -12
- nautobot/dcim/tables/template_code.py +1 -1
- nautobot/dcim/templates/dcim/controller_retrieve.html +18 -35
- nautobot/dcim/templates/dcim/controllerdevicegroup_create.html +43 -0
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +43 -67
- nautobot/dcim/templates/dcim/device.html +2 -10
- nautobot/dcim/templates/dcim/device_edit.html +1 -1
- nautobot/dcim/tests/test_api.py +6 -11
- nautobot/dcim/tests/test_filters.py +81 -92
- nautobot/dcim/tests/test_graphql.py +1 -11
- nautobot/dcim/tests/test_models.py +15 -15
- nautobot/dcim/tests/test_signals.py +0 -2
- nautobot/dcim/tests/test_views.py +12 -24
- nautobot/dcim/urls.py +1 -1
- nautobot/dcim/views.py +15 -19
- nautobot/extras/api/urls.py +2 -1
- nautobot/extras/api/views.py +10 -0
- nautobot/extras/filters/__init__.py +2 -53
- nautobot/extras/forms/contacts.py +0 -7
- nautobot/extras/managers.py +0 -14
- nautobot/extras/navigation.py +65 -71
- nautobot/extras/plugins/views.py +11 -7
- nautobot/extras/tests/test_api.py +0 -2
- nautobot/extras/tests/test_dynamicgroups.py +0 -2
- nautobot/extras/tests/test_filters.py +4 -89
- nautobot/extras/tests/test_models.py +0 -9
- nautobot/extras/tests/test_relationships.py +1 -10
- nautobot/extras/tests/test_views.py +1 -112
- nautobot/extras/views.py +10 -10
- nautobot/ipam/api/urls.py +2 -1
- nautobot/ipam/api/views.py +11 -0
- nautobot/ipam/tables.py +22 -2
- nautobot/ipam/tests/test_graphql.py +3 -2
- nautobot/ipam/tests/test_views.py +0 -1
- nautobot/ipam/views.py +9 -9
- nautobot/project-static/css/base.css +0 -1
- nautobot/project-static/docs/404.html +3 -24
- nautobot/project-static/docs/apps/index.html +3 -24
- nautobot/project-static/docs/apps/nautobot-apps.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +5 -26
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +3 -24
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +3 -242
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +5 -69
- nautobot/project-static/docs/development/apps/api/configuration-view.html +3 -24
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +3 -24
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +3 -24
- nautobot/project-static/docs/development/apps/api/models/global-search.html +3 -24
- nautobot/project-static/docs/development/apps/api/models/graphql.html +3 -24
- nautobot/project-static/docs/development/apps/api/models/index.html +3 -24
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +3 -24
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +3 -24
- nautobot/project-static/docs/development/apps/api/prometheus.html +3 -24
- nautobot/project-static/docs/development/apps/api/setup.html +3 -24
- nautobot/project-static/docs/development/apps/api/testing.html +3 -24
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +3 -24
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +3 -24
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +3 -24
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +3 -24
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/base-template.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +12 -38
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +15 -41
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/index.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/notes.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +3 -24
- nautobot/project-static/docs/development/apps/api/views/urls.html +3 -24
- nautobot/project-static/docs/development/apps/index.html +3 -24
- nautobot/project-static/docs/development/apps/migration/code-updates.html +3 -24
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +3 -24
- nautobot/project-static/docs/development/apps/migration/from-v1.html +3 -24
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +3 -24
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +3 -24
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +3 -24
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +3 -24
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +3 -24
- nautobot/project-static/docs/development/core/application-registry.html +3 -24
- nautobot/project-static/docs/development/core/best-practices.html +3 -24
- nautobot/project-static/docs/development/core/bootstrap-ui.html +3 -24
- nautobot/project-static/docs/development/core/caching.html +3 -24
- nautobot/project-static/docs/development/core/controllers.html +204 -35
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +3 -24
- nautobot/project-static/docs/development/core/extending-models.html +3 -24
- nautobot/project-static/docs/development/core/generic-views.html +3 -24
- nautobot/project-static/docs/development/core/getting-started.html +13 -43
- nautobot/project-static/docs/development/core/homepage.html +3 -24
- nautobot/project-static/docs/development/core/index.html +3 -24
- nautobot/project-static/docs/development/core/model-features.html +3 -24
- nautobot/project-static/docs/development/core/natural-keys.html +3 -24
- nautobot/project-static/docs/development/core/navigation-menu.html +3 -24
- nautobot/project-static/docs/development/core/release-checklist.html +3 -24
- nautobot/project-static/docs/development/core/role-internals.html +3 -24
- nautobot/project-static/docs/development/core/settings.html +3 -24
- nautobot/project-static/docs/development/core/style-guide.html +3 -24
- nautobot/project-static/docs/development/core/templates.html +3 -24
- nautobot/project-static/docs/development/core/testing.html +3 -24
- nautobot/project-static/docs/development/core/user-preferences.html +3 -24
- nautobot/project-static/docs/development/index.html +3 -24
- nautobot/project-static/docs/development/jobs/index.html +3 -24
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +3 -24
- nautobot/project-static/docs/index.html +3 -24
- nautobot/project-static/docs/models/dcim/{controllermanageddevicegroup.html → controllerdevicegroup.html} +3 -3
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/release-notes/index.html +3 -24
- nautobot/project-static/docs/release-notes/version-1.0.html +3 -24
- nautobot/project-static/docs/release-notes/version-1.1.html +3 -24
- nautobot/project-static/docs/release-notes/version-1.2.html +3 -24
- nautobot/project-static/docs/release-notes/version-1.3.html +3 -24
- nautobot/project-static/docs/release-notes/version-1.4.html +3 -24
- nautobot/project-static/docs/release-notes/version-1.5.html +3 -24
- nautobot/project-static/docs/release-notes/version-1.6.html +3 -24
- nautobot/project-static/docs/release-notes/version-2.0.html +3 -24
- nautobot/project-static/docs/release-notes/version-2.1.html +162 -411
- nautobot/project-static/docs/release-notes/version-2.2.html +30 -212
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +255 -260
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +3 -24
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +3 -24
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +3 -24
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +3 -24
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +3 -24
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +3 -24
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +3 -24
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +3 -24
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +3 -24
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +3 -24
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +3 -24
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +3 -24
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +3 -24
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +3 -24
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +3 -24
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +3 -24
- nautobot/project-static/docs/user-guide/administration/installation/docker.html +6 -31
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +3 -24
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -24
- nautobot/project-static/docs/user-guide/administration/installation/index.html +3 -24
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +3 -24
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +3 -24
- nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +6 -27
- nautobot/project-static/docs/user-guide/administration/installation/services.html +3 -24
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +3 -24
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +3 -24
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +3 -24
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +3 -24
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +11 -259
- nautobot/project-static/docs/user-guide/core-data-model/dcim/{controllermanageddevicegroup.html → controllerdevicegroup.html} +17 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +6 -27
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +3 -24
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/{contacts-and-teams.html → contact-and-team.html} +4 -25
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +5 -26
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +5 -26
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +3 -24
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +3 -24
- nautobot/project-static/docs/user-guide/index.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +3 -24
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +3 -24
- nautobot/tenancy/api/urls.py +2 -1
- nautobot/tenancy/api/views.py +12 -0
- nautobot/tenancy/tables.py +1 -1
- nautobot/tenancy/tests/test_views.py +0 -1
- nautobot/users/api/urls.py +2 -1
- nautobot/users/api/views.py +65 -2
- nautobot/users/views.py +8 -8
- nautobot/virtualization/api/urls.py +2 -1
- nautobot/virtualization/api/views.py +12 -0
- {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/METADATA +3 -3
- {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/RECORD +356 -361
- nautobot/core/tests/integration/test_view_authentication.py +0 -67
- nautobot/dcim/management/commands/migrate_location_contacts.py +0 -218
- nautobot/dcim/templates/dcim/controller_create.html +0 -70
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +0 -88
- nautobot/ipam/tests/test_tables.py +0 -42
- nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +0 -8581
- /nautobot/dcim/templates/dcim/{controllermanageddevicegroup_retrieve.html → controllerdevicegroup_retrieve.html} +0 -0
- {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/NOTICE +0 -0
- {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/WHEEL +0 -0
- {nautobot-2.2.0.dist-info → nautobot-2.2.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
from django.test import tag
|
|
2
|
-
|
|
3
|
-
from nautobot.core.testing import TestCase
|
|
4
|
-
from nautobot.core.utils.lookup import get_url_for_url_pattern, get_url_patterns
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@tag("integration")
|
|
8
|
-
class AuthenticationEnforcedTestCase(TestCase):
|
|
9
|
-
r"""
|
|
10
|
-
Test that all\* registered views require authentication to access.
|
|
11
|
-
|
|
12
|
-
\* with a very small number of known exceptions such as login and logout views.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
def test_all_views_require_authentication(self):
|
|
16
|
-
self.client.logout()
|
|
17
|
-
url_patterns = get_url_patterns()
|
|
18
|
-
|
|
19
|
-
for url_pattern in url_patterns:
|
|
20
|
-
with self.subTest(url_pattern=url_pattern):
|
|
21
|
-
url = get_url_for_url_pattern(url_pattern)
|
|
22
|
-
response = self.client.get(url, follow=True)
|
|
23
|
-
|
|
24
|
-
if response.status_code == 405: # Method not allowed
|
|
25
|
-
response = self.client.post(url, follow=True)
|
|
26
|
-
|
|
27
|
-
# Is a view that *should* be open to unauthenticated users?
|
|
28
|
-
if url in [
|
|
29
|
-
"/admin/login/",
|
|
30
|
-
"/api/plugins/example-app/webhook/",
|
|
31
|
-
"/health/",
|
|
32
|
-
"/login/",
|
|
33
|
-
"/media-failure/",
|
|
34
|
-
"/template.css",
|
|
35
|
-
]:
|
|
36
|
-
self.assertHttpStatus(response, 200, msg=url)
|
|
37
|
-
elif response.status_code == 200:
|
|
38
|
-
# UI views generally should redirect unauthenticated users to the appropriate login page
|
|
39
|
-
if url.startswith("/admin"):
|
|
40
|
-
if "logout" in url:
|
|
41
|
-
# /admin/logout/ sets next=/admin/ because having login redirect to logout would be silly
|
|
42
|
-
redirect_url = "/admin/login/?next=/admin/"
|
|
43
|
-
else:
|
|
44
|
-
redirect_url = f"/admin/login/?next={url}"
|
|
45
|
-
else:
|
|
46
|
-
if "logout" in url:
|
|
47
|
-
# /logout/ sets next=/ because having login redirect back to logout would be silly
|
|
48
|
-
redirect_url = "/login/?next=/"
|
|
49
|
-
else:
|
|
50
|
-
redirect_url = f"/login/?next={url}"
|
|
51
|
-
self.assertRedirects(response, redirect_url)
|
|
52
|
-
elif response.status_code != 403:
|
|
53
|
-
if any(
|
|
54
|
-
url.startswith(path)
|
|
55
|
-
for path in [
|
|
56
|
-
"/complete/", # social auth
|
|
57
|
-
"/login/", # social auth
|
|
58
|
-
"/media/", # MEDIA_ROOT
|
|
59
|
-
"/plugins/example-app/docs/", # STATIC_ROOT
|
|
60
|
-
]
|
|
61
|
-
):
|
|
62
|
-
self.assertEqual(response.status_code, 404)
|
|
63
|
-
else:
|
|
64
|
-
self.fail(
|
|
65
|
-
f"Unexpected {response.status_code} response at {url}: "
|
|
66
|
-
+ response.content.decode(response.charset)
|
|
67
|
-
)
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
from textwrap import dedent, indent
|
|
2
|
-
|
|
3
|
-
from django.core.exceptions import ValidationError
|
|
4
|
-
from django.core.management.base import BaseCommand
|
|
5
|
-
from django.db import transaction
|
|
6
|
-
|
|
7
|
-
from nautobot.dcim.models import Location
|
|
8
|
-
from nautobot.extras.filters import ContactFilterSet, TeamFilterSet
|
|
9
|
-
from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Team
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Command(BaseCommand):
|
|
13
|
-
help = "Migrate Location contact fields to Contact and Team objects."
|
|
14
|
-
verbose_help = """
|
|
15
|
-
This command will present a series of prompts to guide you through migrating Locations that
|
|
16
|
-
have data in the `contact_name`, `contact_phone`, or `contact_email` fields which are not
|
|
17
|
-
already associated to a Contact or Team. This command will give you the option to create new
|
|
18
|
-
Contacts or Teams or, if a similar Contact or Team already exists, to link the Location to the
|
|
19
|
-
existing Contact or Team. Note that when assigning a Location to an existing Contact or Team
|
|
20
|
-
that has a blank `phone` or `email` field, the value from the Location will be copied to the
|
|
21
|
-
Contact/Team. After a Location has been associated to a Contact or Team, the `contact_name`,
|
|
22
|
-
`contact_phone`, and `contact_email` fields will be cleared from the Location.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
def handle(self, *args, **kwargs):
|
|
26
|
-
status_role_err_msg = "No {0} found for the ContactAssociation content type. Please ensure {0} are created before running this command."
|
|
27
|
-
if not Status.objects.get_for_model(ContactAssociation).exists():
|
|
28
|
-
self.stdout.write(self.style.ERROR(status_role_err_msg.format("statuses")))
|
|
29
|
-
return
|
|
30
|
-
if not Role.objects.get_for_model(ContactAssociation).exists():
|
|
31
|
-
self.stdout.write(self.style.ERROR(status_role_err_msg.format("roles")))
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
self.stdout.write(self.style.NOTICE(" ".join(dedent(self.verbose_help).split("\n"))))
|
|
35
|
-
|
|
36
|
-
try:
|
|
37
|
-
with transaction.atomic():
|
|
38
|
-
try:
|
|
39
|
-
self.migrate_location_contacts()
|
|
40
|
-
except KeyboardInterrupt:
|
|
41
|
-
while True:
|
|
42
|
-
rollback = input("\nRoll back changes? [y/n]: ").strip().lower()
|
|
43
|
-
if rollback == "y":
|
|
44
|
-
raise
|
|
45
|
-
elif rollback == "n":
|
|
46
|
-
break
|
|
47
|
-
except KeyboardInterrupt:
|
|
48
|
-
self.stdout.write(self.style.ERROR("\nOperation cancelled, all changes rolled back."))
|
|
49
|
-
except:
|
|
50
|
-
self.stdout.write(self.style.ERROR("\nOperation failed, all changes rolled back."))
|
|
51
|
-
raise
|
|
52
|
-
|
|
53
|
-
def migrate_location_contacts(self):
|
|
54
|
-
"""Iterate through Locations with contact information and try to match to existing Contact or Team."""
|
|
55
|
-
locations_with_contact_data = (
|
|
56
|
-
Location.objects.without_tree_fields()
|
|
57
|
-
.exclude(
|
|
58
|
-
contact_name="",
|
|
59
|
-
contact_phone="",
|
|
60
|
-
contact_email="",
|
|
61
|
-
)
|
|
62
|
-
.filter(associated_contacts__isnull=True)
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
for location in locations_with_contact_data:
|
|
66
|
-
self.stdout.write(f"Finding existing Contacts or Teams for location {location.display}...")
|
|
67
|
-
selected_contact = None
|
|
68
|
-
similar_contacts = list(ContactFilterSet(data={"similar_to_location_data": [location]}).qs)
|
|
69
|
-
similar_teams = list(TeamFilterSet(data={"similar_to_location_data": [location]}).qs)
|
|
70
|
-
similar_contacts_and_teams = similar_contacts + similar_teams
|
|
71
|
-
|
|
72
|
-
if not similar_contacts_and_teams:
|
|
73
|
-
self.stdout.write(
|
|
74
|
-
self.style.WARNING(f"No similar Contacts or Teams found for location {location.display}.")
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
else:
|
|
78
|
-
# Found similar contacts or teams, prompt user for action
|
|
79
|
-
self.stdout.write("")
|
|
80
|
-
self.stdout.write(self.style.WARNING(f"Found similar contacts/teams for location {location.display}:"))
|
|
81
|
-
self.stdout.write(f" current contact name: {location.contact_name!r}")
|
|
82
|
-
self.stdout.write(f" current contact phone: {location.contact_phone!r}")
|
|
83
|
-
self.stdout.write(f" current contact email: {location.contact_email!r}")
|
|
84
|
-
self.stdout.write("")
|
|
85
|
-
|
|
86
|
-
# Output menu of choices of valid contacts/teams
|
|
87
|
-
for i, contact in enumerate(similar_contacts_and_teams, start=1):
|
|
88
|
-
self.stdout.write(f"{self.style.WARNING(i)}: {contact._meta.model_name.title()}: {contact.name}")
|
|
89
|
-
self.print_contact_fields(contact, rjust=len(str(i)) + len(contact._meta.model_name) + 2)
|
|
90
|
-
self.stdout.write("")
|
|
91
|
-
|
|
92
|
-
self.stdout.write(self.style.WARNING("c") + ": Create a new Contact")
|
|
93
|
-
self.stdout.write(self.style.WARNING("t") + ": Create a new Team")
|
|
94
|
-
self.stdout.write(self.style.WARNING("s") + ": Skip this location")
|
|
95
|
-
|
|
96
|
-
# Retrieve desired contact/team from user input
|
|
97
|
-
while True:
|
|
98
|
-
choice = input("Select a choice from the list of items: ")
|
|
99
|
-
if choice == "s":
|
|
100
|
-
self.stdout.write(f"Skipping location {location.display}")
|
|
101
|
-
break
|
|
102
|
-
elif choice == "c":
|
|
103
|
-
selected_contact = self.create_new_contact_from_location(location, model=Contact)
|
|
104
|
-
break
|
|
105
|
-
elif choice == "t":
|
|
106
|
-
selected_contact = self.create_new_contact_from_location(location, model=Team)
|
|
107
|
-
break
|
|
108
|
-
elif choice.lower() == "q":
|
|
109
|
-
raise KeyboardInterrupt
|
|
110
|
-
elif choice.isdigit() and 0 < int(choice) <= len(similar_contacts_and_teams):
|
|
111
|
-
selected_contact = similar_contacts_and_teams[int(choice) - 1]
|
|
112
|
-
break
|
|
113
|
-
|
|
114
|
-
if selected_contact is not None:
|
|
115
|
-
self.associate_contact_to_location(selected_contact, location)
|
|
116
|
-
|
|
117
|
-
def associate_contact_to_location(self, contact, location):
|
|
118
|
-
role, status = self.prompt_for_role_and_status()
|
|
119
|
-
|
|
120
|
-
# If email or phone fields are present in the location but not the contact, update the contact fields
|
|
121
|
-
updated_fields = {}
|
|
122
|
-
if location.contact_phone and not contact.phone:
|
|
123
|
-
contact.phone = location.contact_phone
|
|
124
|
-
updated_fields["phone"] = location.contact_phone
|
|
125
|
-
if location.contact_email and not contact.email:
|
|
126
|
-
contact.email = location.contact_email
|
|
127
|
-
updated_fields["email"] = location.contact_email
|
|
128
|
-
if updated_fields:
|
|
129
|
-
try:
|
|
130
|
-
contact.validated_save()
|
|
131
|
-
self.stdout.write(
|
|
132
|
-
self.style.SUCCESS(
|
|
133
|
-
f"Updated {contact._meta.model_name.title()} {contact.name} field(s): {updated_fields!r}"
|
|
134
|
-
)
|
|
135
|
-
)
|
|
136
|
-
except ValidationError as e:
|
|
137
|
-
contact.refresh_from_db()
|
|
138
|
-
self.stdout.write(
|
|
139
|
-
self.style.ERROR(f"Attempted to update {contact!r} field(s) but failed: {updated_fields!r}: {e}")
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
try:
|
|
143
|
-
contact_association = ContactAssociation(
|
|
144
|
-
contact=contact if isinstance(contact, Contact) else None,
|
|
145
|
-
team=contact if isinstance(contact, Team) else None,
|
|
146
|
-
associated_object=location,
|
|
147
|
-
role=role,
|
|
148
|
-
status=status,
|
|
149
|
-
)
|
|
150
|
-
contact_association.validated_save()
|
|
151
|
-
location.contact_name = ""
|
|
152
|
-
location.contact_email = ""
|
|
153
|
-
location.contact_phone = ""
|
|
154
|
-
location.validated_save()
|
|
155
|
-
except Exception as e:
|
|
156
|
-
self.stdout.write(self.style.ERROR(f"Failed to create association: {e}"))
|
|
157
|
-
else:
|
|
158
|
-
self.stdout.write(self.style.SUCCESS(f"Associated {contact!r} to location {location.display}"))
|
|
159
|
-
|
|
160
|
-
def prompt_for_role_and_status(self):
|
|
161
|
-
# Prompt for role
|
|
162
|
-
self.stdout.write("\nValid roles for this association:")
|
|
163
|
-
valid_roles = list(Role.objects.get_for_model(ContactAssociation))
|
|
164
|
-
for i, role in enumerate(valid_roles, start=1):
|
|
165
|
-
self.stdout.write(self.style.WARNING(f"{i}") + f": {role}")
|
|
166
|
-
while True:
|
|
167
|
-
selected_role = input("Select a role for this association: ")
|
|
168
|
-
if selected_role.isdigit() and 0 < int(selected_role) <= len(valid_roles):
|
|
169
|
-
role = valid_roles[int(selected_role) - 1]
|
|
170
|
-
break
|
|
171
|
-
|
|
172
|
-
# Prompt for status
|
|
173
|
-
self.stdout.write("\nValid statuses for this association:")
|
|
174
|
-
valid_statuses = list(Status.objects.get_for_model(ContactAssociation))
|
|
175
|
-
for i, status in enumerate(valid_statuses, start=1):
|
|
176
|
-
self.stdout.write(self.style.WARNING(f"{i}") + f": {status}")
|
|
177
|
-
while True:
|
|
178
|
-
selected_status = input("Select a status for this association: ")
|
|
179
|
-
if selected_status.isdigit() and 0 < int(selected_status) <= len(valid_statuses):
|
|
180
|
-
status = valid_statuses[int(selected_status) - 1]
|
|
181
|
-
break
|
|
182
|
-
|
|
183
|
-
return role, status
|
|
184
|
-
|
|
185
|
-
def create_new_contact_from_location(self, location, model):
|
|
186
|
-
"""Create a new Contact or Team from the location's contact data."""
|
|
187
|
-
|
|
188
|
-
self.stdout.write(f"Creating new {model._meta.model_name.title()} for location {location.display}...")
|
|
189
|
-
name = location.contact_name
|
|
190
|
-
phone = location.contact_phone
|
|
191
|
-
email = location.contact_email
|
|
192
|
-
self.stdout.write(f" contact name: {name!r}")
|
|
193
|
-
self.stdout.write(f" contact phone: {phone!r}")
|
|
194
|
-
self.stdout.write(f" contact email: {email!r}")
|
|
195
|
-
|
|
196
|
-
while not name:
|
|
197
|
-
name = input(f"Name is required. Enter a name for the new {model._meta.model_name.title()}: ")
|
|
198
|
-
|
|
199
|
-
try:
|
|
200
|
-
contact = model(
|
|
201
|
-
name=name,
|
|
202
|
-
phone=phone,
|
|
203
|
-
email=email,
|
|
204
|
-
)
|
|
205
|
-
contact.validated_save()
|
|
206
|
-
return contact
|
|
207
|
-
except Exception as e:
|
|
208
|
-
self.stdout.write(self.style.ERROR(f"Failed to create {model._meta.model_name.title()}: {e}"))
|
|
209
|
-
return None
|
|
210
|
-
|
|
211
|
-
def print_contact_fields(self, contact, rjust=0):
|
|
212
|
-
for field_name in ["phone", "email"]:
|
|
213
|
-
if getattr(contact, field_name):
|
|
214
|
-
self.stdout.write(
|
|
215
|
-
field_name.title().rjust(rjust)
|
|
216
|
-
+ ": "
|
|
217
|
-
+ indent(getattr(contact, field_name), " " * (rjust + 2)).lstrip()
|
|
218
|
-
)
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
{% extends 'generic/object_create.html' %}
|
|
2
|
-
{% load form_helpers %}
|
|
3
|
-
|
|
4
|
-
{% block form %}
|
|
5
|
-
<div class="panel panel-default">
|
|
6
|
-
<div class="panel-heading"><strong>Controller</strong></div>
|
|
7
|
-
<div class="panel-body">
|
|
8
|
-
{% render_field form.name %}
|
|
9
|
-
{% render_field form.status %}
|
|
10
|
-
{% render_field form.role %}
|
|
11
|
-
{% render_field form.location %}
|
|
12
|
-
{% render_field form.platform %}
|
|
13
|
-
{% render_field form.description %}
|
|
14
|
-
</div>
|
|
15
|
-
</div>
|
|
16
|
-
<div class="panel panel-default">
|
|
17
|
-
<div class="panel-heading"><strong>Integration</strong></div>
|
|
18
|
-
<div class="panel-body">
|
|
19
|
-
{% render_field form.external_integration %}
|
|
20
|
-
{% with controllerdevice_tab_active=form.initial.controller_device %}
|
|
21
|
-
<ul class="nav nav-tabs" role="tablist">
|
|
22
|
-
<li role="presentation"{% if controllerdevice_tab_active %} class="active"{% endif %}><a href="#controllerdevice" role="tab" data-toggle="tab">Controller Device</a></li>
|
|
23
|
-
<li role="presentation"{% if not controllerdevice_tab_active %} class="active"{% endif %}><a href="#controller_device_redundancy_group" role="tab" data-toggle="tab">Controller Device Redundancy Group</a></li>
|
|
24
|
-
</ul>
|
|
25
|
-
<div class="tab-content">
|
|
26
|
-
<div class="tab-pane{% if not controllerdevice_tab_active %} active{% endif %}" id="controller_device_redundancy_group">
|
|
27
|
-
{% render_field form.controller_device_redundancy_group %}
|
|
28
|
-
</div>
|
|
29
|
-
<div class="tab-pane{% if controllerdevice_tab_active %} active{% endif %}" id="controllerdevice">
|
|
30
|
-
{% render_field form.controller_device %}
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
|
-
{% endwith %}
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
<div class="panel panel-default">
|
|
37
|
-
<div class="panel-heading"><strong>Tenancy</strong></div>
|
|
38
|
-
<div class="panel-body">
|
|
39
|
-
{% render_field form.tenant_group %}
|
|
40
|
-
{% render_field form.tenant %}
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
{% include 'inc/extras_features_edit_form_fields.html' %}
|
|
44
|
-
{% endblock form %}
|
|
45
|
-
|
|
46
|
-
{% block javascript %}
|
|
47
|
-
<script type="text/javascript">
|
|
48
|
-
const controllerDevice = $('#id_controller_device');
|
|
49
|
-
const controllerDeviceRedundancyGroup = $('#id_controller_device_redundancy_group');
|
|
50
|
-
|
|
51
|
-
const controllerUpdated = function() {
|
|
52
|
-
if (controllerDevice.val()) {
|
|
53
|
-
controllerDeviceRedundancyGroup.val('');
|
|
54
|
-
controllerDeviceRedundancyGroup.trigger('change');
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const controllerDeviceRedundancyGroupUpdated = function() {
|
|
59
|
-
if (controllerDeviceRedundancyGroup.val()) {
|
|
60
|
-
controllerDevice.val('');
|
|
61
|
-
controllerDevice.trigger('change');
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
controllerDevice.on('select2:select', controllerUpdated);
|
|
66
|
-
controllerDevice.on('select2:unselect', controllerUpdated);
|
|
67
|
-
controllerDeviceRedundancyGroup.on('select2:select', controllerDeviceRedundancyGroupUpdated);
|
|
68
|
-
controllerDeviceRedundancyGroup.on('select2:unselect', controllerDeviceRedundancyGroupUpdated);
|
|
69
|
-
</script>
|
|
70
|
-
{% endblock %}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
{% extends 'generic/object_create.html' %}
|
|
2
|
-
{% load form_helpers %}
|
|
3
|
-
{% load helpers %}
|
|
4
|
-
{% load static %}
|
|
5
|
-
|
|
6
|
-
{% block form %}
|
|
7
|
-
<div class="panel panel-default">
|
|
8
|
-
<div class="panel-heading"><strong>Controller</strong></div>
|
|
9
|
-
<div class="panel-body">
|
|
10
|
-
{% render_field form.controller.as_hidden %}
|
|
11
|
-
{% render_field form.controller %}
|
|
12
|
-
{% render_field form.name %}
|
|
13
|
-
{% render_field form.devices %}
|
|
14
|
-
{% render_field form.parent %}
|
|
15
|
-
{% render_field form.weight %}
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
{% include 'inc/extras_features_edit_form_fields.html' %}
|
|
19
|
-
{% endblock form %}
|
|
20
|
-
|
|
21
|
-
{% block javascript %}
|
|
22
|
-
<script type="text/javascript">
|
|
23
|
-
const controllerUrl = '{% url 'dcim-api:controller-list' %}';
|
|
24
|
-
const controllerGroupUrl = '{% url 'dcim-api:controllermanageddevicegroup-list' %}';
|
|
25
|
-
|
|
26
|
-
const parentInput = $('select#id_parent');
|
|
27
|
-
const controllerInput = $('select#id_controller');
|
|
28
|
-
const hiddenControllerInput = $('input#id_controller');
|
|
29
|
-
|
|
30
|
-
const request = (url, calback) => {
|
|
31
|
-
$.ajax({
|
|
32
|
-
url,
|
|
33
|
-
method: "GET",
|
|
34
|
-
success: calback,
|
|
35
|
-
error: (error) => {
|
|
36
|
-
const message = `Error in request. URL: ${url}`;
|
|
37
|
-
console.error(message, error);
|
|
38
|
-
alert(message);
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const setController = (uid) => {
|
|
44
|
-
console.log('Set controller:', uid);
|
|
45
|
-
if (controllerInput.find(`option[value=${uid}]`).length) {
|
|
46
|
-
controllerInput.val(uid);
|
|
47
|
-
hiddenControllerInput.val(uid);
|
|
48
|
-
} else {
|
|
49
|
-
request(`${controllerUrl}${uid}/`, (data) => {
|
|
50
|
-
console.log('Controller data:', data);
|
|
51
|
-
controllerInput.append(new Option(data.name, uid));
|
|
52
|
-
setController(uid);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const updateControllerFromParent = (parentUid) => {
|
|
58
|
-
request(
|
|
59
|
-
`${controllerGroupUrl}${parentUid}/`,
|
|
60
|
-
(data) => data.controller && setController(data.controller.id),
|
|
61
|
-
);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const onParentInput = (event) => {
|
|
65
|
-
const parentUid = parentInput.val();
|
|
66
|
-
if (parentUid) {
|
|
67
|
-
controllerInput.prop("disabled", true);
|
|
68
|
-
if (event) {
|
|
69
|
-
updateControllerFromParent(parentUid);
|
|
70
|
-
}
|
|
71
|
-
} else {
|
|
72
|
-
controllerInput.prop("disabled", false);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const onControllerInput = () => {
|
|
77
|
-
hiddenControllerInput.val(controllerInput.val());
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
parentInput.on('select2:select', onParentInput);
|
|
81
|
-
parentInput.on('select2:unselect', onParentInput);
|
|
82
|
-
|
|
83
|
-
controllerInput.on('select2:select', onControllerInput);
|
|
84
|
-
controllerInput.on('select2:unselect', onControllerInput);
|
|
85
|
-
|
|
86
|
-
onParentInput();
|
|
87
|
-
</script>
|
|
88
|
-
{% endblock %}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
from django.test import TestCase
|
|
2
|
-
|
|
3
|
-
from nautobot.core.models.querysets import count_related
|
|
4
|
-
from nautobot.dcim.models.locations import Location
|
|
5
|
-
from nautobot.ipam.models import Prefix
|
|
6
|
-
from nautobot.ipam.tables import PrefixTable
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class PrefixTableTestCase(TestCase):
|
|
10
|
-
def _validate_sorted_queryset_same_with_table_queryset(self, queryset, table_class, field_name):
|
|
11
|
-
with self.subTest(f"Assert sorting {table_class.__name__} on '{field_name}'"):
|
|
12
|
-
table = table_class(queryset, order_by=field_name)
|
|
13
|
-
table_queryset_data = table.data.data.values_list("pk", flat=True)
|
|
14
|
-
sorted_queryset = queryset.order_by(field_name).values_list("pk", flat=True)
|
|
15
|
-
self.assertEqual(list(table_queryset_data), list(sorted_queryset))
|
|
16
|
-
|
|
17
|
-
def test_prefix_table_sort(self):
|
|
18
|
-
"""Assert TreeNode model table are orderable."""
|
|
19
|
-
# Due to MySQL's lack of support for combining 'LIMIT' and 'ORDER BY' in a single query,
|
|
20
|
-
# hence this approach.
|
|
21
|
-
pk_list = Prefix.objects.all().values_list("pk", flat=True)[:20]
|
|
22
|
-
pk_list = [str(pk) for pk in pk_list]
|
|
23
|
-
queryset = Prefix.objects.filter(pk__in=pk_list)
|
|
24
|
-
|
|
25
|
-
# Assets model names
|
|
26
|
-
table_avail_fields = ["tenant", "vlan", "namespace"]
|
|
27
|
-
for table_field_name in table_avail_fields:
|
|
28
|
-
self._validate_sorted_queryset_same_with_table_queryset(queryset, PrefixTable, table_field_name)
|
|
29
|
-
self._validate_sorted_queryset_same_with_table_queryset(queryset, PrefixTable, f"-{table_field_name}")
|
|
30
|
-
|
|
31
|
-
# Assert `prefix`
|
|
32
|
-
table_queryset_data = PrefixTable(queryset, order_by="prefix").data.data.values_list("pk", flat=True)
|
|
33
|
-
prefix_queryset = queryset.order_by("network", "prefix_length").values_list("pk", flat=True)
|
|
34
|
-
self.assertEqual(list(table_queryset_data), list(prefix_queryset))
|
|
35
|
-
table_queryset_data = PrefixTable(queryset, order_by="-prefix").data.data.values_list("pk", flat=True)
|
|
36
|
-
prefix_queryset = queryset.order_by("-network", "-prefix_length").values_list("pk", flat=True)
|
|
37
|
-
self.assertEqual(list(table_queryset_data), list(prefix_queryset))
|
|
38
|
-
|
|
39
|
-
# Assets `location_count`
|
|
40
|
-
location_count_queryset = queryset.annotate(location_count=count_related(Location, "prefixes")).all()
|
|
41
|
-
self._validate_sorted_queryset_same_with_table_queryset(location_count_queryset, PrefixTable, "location_count")
|
|
42
|
-
self._validate_sorted_queryset_same_with_table_queryset(location_count_queryset, PrefixTable, "-location_count")
|